嵌入式Linux:子进程执行新程序

共 6380字,需浏览 13分钟

 ·

2024-12-02 08:00

点击上方蓝色字体,关注我们

在 Linux 中,子进程在创建后可以通过 exec 系列系统调用执行一个全新的程序。


这种情况下,子进程会替换原有的代码和数据段,运行一个新的可执行程序,但它的进程 ID(PID)保持不变。


exec 系列调用包括多个变体,常见的有 execl()、execv()、execle()、execve() 等,它们的主要区别在于参数传递方式不同。


子进程执行新程序的流程如下:

  • 创建子进程使用 fork() 创建子进程。

  • 调用 exec在子进程中调用 exec 执行新程序。

  • 替换子进程的内存映像exec 会替换子进程的整个内存空间,包括代码段、数据段、堆栈等,只保留进程的 PID 和一些特定属性。

  • 父进程继续执行父进程保持不变,继续执行它的代码,直到调用 wait() 等待子进程结束。


exec 系列函数通过不同的方式传递参数和环境变量,能够实现灵活的程序替换。


具体函数如下:

  • execve() 是最基础的调用,允许自定义环境变量和参数。

  • execl() 和 execv() 提供了简化接口,execl() 使用可变参数,execv() 使用参数数组。

  • execlp() 和 execvp() 可以根据 PATH 环境变量搜索程序。

  • execle() 和 execvpe() 提供自定义环境变量的支持。


1


execve()

execve() 是最基础的 exec 函数,所有其他 exec 系列函数都是基于它的。它直接接受路径名、参数数组和环境变量数组。


函数原型如下:


int execve(const char *filename, char *const argv[], char *const envp[]);


参数如下:

  • filename:要执行的文件的路径(绝对或相对路径)。

  • argv[]:参数列表(传递给程序的命令行参数)。第一个参数通常是程序本身的名称。

  • envp[]:环境变量列表。


使用 execve() 执行 /bin/ls,传递了参数 -l 和环境变量 PATH。


int main(void) {    char *argv[] = {"ls", "-l", NULL};      // 参数列表    char *envp[] = {"PATH=/bin", NULL};     // 环境变量
printf("执行 ls 程序\n");
if (execve("/bin/ls", argv, envp) == -1) { perror("execve error"); }
return 0;}

2


execl()

execl() 是 execve() 的简化版本,参数以可变长度的方式传递(列表形式)。最后一个参数必须是 (char *) NULL。


函数原型如下:


int execl(const char *path, const char *arg, ... /* (char *) NULL */);


参数如下:

  • path:可执行文件的路径。

  • arg:程序名称后接任意数量的参数,最后以 NULL 结束。


以下例子调用 execl(),通过可变参数传递给 ls 程序。


int main(void) {    printf("使用 execl 执行 ls 程序\n");    execl("/bin/ls", "ls", "-l", (char *) NULL);  // 传递可变长度参数列表
// 如果 exec 调用失败,返回 -1 perror("execl error"); return 0;}

3


execlp()

execlp() 和 execl() 类似,但它不需要提供文件的完整路径。它会在 PATH 环境变量指定的目录中搜索可执行文件。


函数原型如下:


int execlp(const char *file, const char *arg, ... /* (char *) NULL */);


参数如下:

  • file:要执行的程序名(无需完整路径)。

  • arg:程序名称及其他参数,最后以 NULL 结束。


以下例中,execlp() 会根据 PATH 环境变量查找 ls 程序的路径。


int main(void) {    printf("使用 execlp 执行 ls 程序\n");    execlp("ls", "ls", "-l", (char *) NULL);  // 使用环境变量中的路径搜索 ls 程序
perror("execlp error"); return 0;}

4


execle()

execle() 类似于 execl(),但允许传递环境变量数组。


函数原型如下:


int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);


参数如下:

  • path:可执行文件路径。

  • arg:程序名称及其他参数,最后以 NULL 结束。

  • envp[]:环境变量数组。


以下例子中,execle() 将自定义的环境变量传递给 ls 程序。


int main(void) {    char *envp[] = {"PATH=/bin", NULL};   // 设置环境变量
printf("使用 execle 执行 ls 程序\n"); execle("/bin/ls", "ls", "-l", (char *) NULL, envp); // 传递环境变量
perror("execle error"); return 0;}

5


execv()

execv() 是 execve() 的简化版本,不需要传递环境变量,只需要路径和参数数组。


函数原型如下:


int execv(const char *path, char *const argv[]);


参数如下:

  • path:可执行文件的路径。

  • argv[]:参数数组。


在该例中,execv() 使用参数数组执行 ls。


int main(void) {    char *argv[] = {"ls", "-l", NULL};  // 参数数组
printf("使用 execv 执行 ls 程序\n"); execv("/bin/ls", argv); // 传递参数数组
perror("execv error"); return 0;}

6


execvp()

execvp() 和 execv() 类似,但它会根据 PATH 环境变量查找可执行文件。


函数原型如下:


int execvp(const char *file, char *const argv[]);


参数如下:

  • file:可执行文件的名称。

  • argv[]:参数数组。


execvp() 不要求完整路径,会自动在 PATH 中查找 ls。


int main(void) {    char *argv[] = {"ls", "-l", NULL};  // 参数数组
printf("使用 execvp 执行 ls 程序\n"); execvp("ls", argv); // 根据 PATH 搜索并执行程序
perror("execvp error"); return 0;}

7


execvpe()

execvpe() 是 execvp() 的扩展,允许传递自定义的环境变量。


函数原型如下:


int execvpe(const char *file, char *const argv[], char *const envp[]);


参数如下:

  • file:要执行的程序名称。

  • argv[]:参数数组。

  • envp[]:环境变量数组。


在以下示例中,execvpe() 使用自定义环境变量执行程序。


int main(void) {    char *argv[] = {"ls", "-l", NULL};  // 参数数组    char *envp[] = {"PATH=/bin", NULL}; // 环境变量
printf("使用 execvpe 执行 ls 程序\n"); execvpe("ls", argv, envp); // 使用 PATH 环境变量执行程序
perror("execvpe error"); return 0;}


子进程执行新程序时,可以通过 exec 系列系统调用替换子进程的内存空间,执行新的二进制程序。


exec 系列调用的不同变体提供了灵活的参数传递方式,适应不同场景需求。


通过合理使用 fork() 和 exec(),可以实现高效的多进程编程,确保资源的有效利用和进程的灵活控制。

点击阅读原文,更精彩~

浏览 23
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报