Linux 创建子进程执行任务(2)

使用 fork/vfork 创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往需要调用一个 exec 族函数以执行另外一个程序。当进程调用 exec 族函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的起始处开始执行。调用 exec 族函数并不创建新进程,所以调用 exec 族函数前后该进程的 PID 并不改变。

exec 族函数一共有六个:

#include <unistd.h> int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);

函数名字中带字母 "l" 的表示其参数个数不确定,带字母 "v" 的表示使用字符串数组指针 argv 指向参数列表。
函数名字中含有字母 "p" 的表示可以自动在环境变量 PATH 指定的路径中搜索要执行的程序。
函数名字中含有字母 "e" 的函数比其它函数多一个参数 envp。该参数是字符串数组指针,用于指定环境变量。调用这样的函数时,可以由用户自行设定子进程的环境变量,存放在参数 envp 所指向的字符串数组中。

事实上,只有 execve 是真正的系统调用,其它五个函数最终都调用 execve。这些函数之间的关系如下图所示(此图来自互联网):

Linux 创建子进程执行任务

exec 族函数的特征:调用 exec 族函数会把新的程序装载到当前进程中。在调用过 exec 族函数后,进程中执行的代码就与之前完全不同了,所以 exec 函数调用之后的代码是不会被执行的。

在子进程中执行任务

下面让我们通过 vfork 和 execve 函数实现在子进程中执行 ls 命令:

#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(void) { pid_t pid; if((pid=vfork()) < 0) { printf("vfork error!\n"); exit(1); } else if(pid==0) { printf("Child process PID: %d.\n", getpid()); char *argv[ ]={"ls", "-al", "/home", NULL}; char *envp[ ]={"PATH=/bin", NULL}; if(execve("/bin/ls", argv, envp) < 0) { printf("subprocess error"); exit(1); } // 子进程要么从 ls 命令中退出,要么从上面的 exit(1) 语句退出 // 所以代码的执行路径永远也走不到这里,下面的 printf 语句不会被执行 printf("You should never see this message."); } else { printf("Parent process PID: %d.\n", getpid()); sleep(1); } return 0; }

把上面的代码保存到文件 subprocessdemo.c 文件中,并执行下面的命令编译:

$ gcc subprocessdemo.c -o subprocessdemo

然后运行编译出来的 subprocessdemo程序:

$ ./subprocessdemo

Linux 创建子进程执行任务

总结

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/13038.html