进程的创建中介绍了 fork()
函数用于创建一个新的进程,新进程被称为子进程。该子进程几乎复制了父进程的全部内容。通过在子进程执行代码区,添加任务代码,可以让子进程完成其他的任务。而在 Linux
中,有另外一种函数接口,提供了在一个进程中执行另一个进程的方法,可以将其称之为 exec
函数族。他可以根据制定的文件名或目录名找到可执行文件,并用它来取代当前进程的数据段、代码段和堆栈段。在执行完之后,当前进程除进程号外,其他内容都被替换了。这里的可执行文件既可以是二进制文件,也可以是 Linux
下任何可执行的文件脚本。
在 Linux
中使用 exec
函数族主要有两种情况:
- 当进程不能在系统中发挥更多的作用时,就可以调用
exec
函数族中的任一一个函数取代当前进程完成后续的工作。 - 如果一个进程想执行另一个程序,那么它可以调用
fork()
函数新建一个进程,然后调用exec
函数族中的任意一个函数,这样看起来就像通过执行应用程序而产生一个新进程(这种情况非常普遍)
exec
函数族原型如下:
#include <unistd.h> extern char **environ; 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 *file, char *const argv[], char *const envp[]);点击复制复制失败已复制
这 6
个函数在函数名和使用语法的规则上都有细微的区别,参数区别如下表所示:
函数 | 第一个参数 | 第二个参数 | 第三个参数 | 第四个参数 |
execl |
path 路径名 |
*arg |
... 附件参数 | \ |
execp |
file 文件名 |
*arg |
... 附件参数 | \ |
exece |
path 路径名 |
*arg |
... 附件参数 | *envp[] |
execv |
path 路径名 |
*argv[] |
\ | \ |
execp |
file 文件名 |
*argv[] |
\ | \ |
execv |
file 文件名 |
*argv[] |
\ | *envp[] |
函数第一个参数有两种传参方式:第一种为路径名,即指定文件名时,需要指定文件名所在路径;第二种为文件名,即只需要指定要执行的二进制文件名或 Linux
下可执行的脚本文件文件名。第三个参数的第二个与第三个参数用来进行列举式传参,都可以传入需要的字符串;后三个函数将第二个参数和第三个参数合并将所有参数通过一个指针数组进行传递。第四个参数用来指定当前进程所使用的环境变量。
execl()函数测试
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { if (execl("/bin/ls", "ls", "./file", NULL) < 0) { perror("execl error"); return -1; } return 0; } 点击复制复制失败已复制
示例中,第一个参数指定可执行文件的路径名;第二个参数为列举式传参,本次传入两个字符串 "ls"
、 "./file"
,其中 "./file"
为当前目录下的 file
目录;第三个附件参数传递为 NULL
。
运行结果如下所示:
$ gcc main.c && ./a.out 1.txt 2.txt 3.txt点击复制复制失败已复制
源码文件结构如下所示:
. ├── file │ ├── 1.txt │ ├── 2.txt │ └── 3.txt └── main.c点击复制复制失败已复制
由此可以看出,此时运行 a.out
产生新进程为 "ls"
,并查看 file
目录下所有的文件。功能与 Shell
命令 "ls + 目录名"
一样。如果代码将 "./file"
参数去掉,则此时新进程将查看当前目录下所有的文件;也可添加参数 "-l"
,查看更详细的内容,如下所示:
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { if (execl("/bin/ls", "ls", "-l", "./file", NULL) < 0) { perror("execl error"); return -1; } return 0; } 点击复制复制失败已复制
编译运行,结果如下:
$ gcc main.c && ./a.out 总用量 0 -rw-rw-r-- 1 iric iric 0 9月 26 19:37 1.txt -rw-rw-r-- 1 iric iric 0 9月 26 19:37 2.txt -rw-rw-r-- 1 iric iric 0 9月 26 19:37 3.txt点击复制复制失败已复制
可以看出第二个参数 char *arg
并非只用于接收一个字符串。
execv()函数测试
下面通过 execv()
函数,展示如何列举式传参,同样也可以将刚才的代码,采用指针数组的形式进行传参,如下所示:
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { char *array[4] = {"ls", "-l", "./file", NULL}; if (execv("/bin/ls", array) < 0) { perror("execv error"); return -1; } return 0; }点击复制复制失败已复制
可以看出将刚才的列举参数,一并放入到指针数组中即可。运行结果和上面一致。