编程语言
634
在项目开发过程中,有时候有这种需求,需要通过现在运行的进程启动磁盘上的另一个可执行程序,也就是通过一个进程启动另一个进程,这种情况下我们可以使用 exec族函数
//函数原型 #include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ... /* (char *) NULL */); int execlp(const char *file, const char *arg, ... /* (char *) NULL */); int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execvpe(const char *file, char *const argv[], char *const envp[]);
这些函数执行成功后不会返回
,因为调用进程的实体,包括代码段
,数据段
和堆栈
等都已经被新的内容取代(也就是说用户区数据基本全部被替换掉了),只留下进程ID等一些表面上的信息仍保持原样.
只有调用失败了,它们才会返回一个 -1,从原程序的调用点接着往下执行。
也就是说exec族函数并没有创建新进程的能力 让启动的新进程寄生到自己虚拟地址空间之内,并挖空了自己的地址空间用户区,把新启动的进程数据填充进去。
exec族函数中最常用的有两个execl()
和execlp()
,这两个函数是对其他4个函数做了进一步的封装,介绍一下。
1. execl()
该函数可用于执行任意一个可执行程序,函数需要通过指定的文件路径才能找到这个可执行程序。
#include <unistd.h> // 变参函数 int execl(const char *path, const char *arg, ...);
- 参数:
-
path
: 要启动的可执行程序的路径, 推荐使用绝对路径 -
arg
: ps aux 查看进程的时候, 启动的进程的名字, 可以随意指定, 一般和要启动的可执行程序名相同 -
...
: 要执行的命令需要的参数,可以写多个,最后以 NULL 结尾,表示参数指定完了。
-
- 返回值:如果这个函数执行成功, 没有返回值,如果执行失败, 返回 -1
2. execlp()
该函数常用于执行已经设置了环境变量的可执行程序,函数中的 p
就是path
,也是说这个函数会自动搜索系统的环境变量PATH
因此使用这个函数执行可执行程序不需要指定路径,只需要指定出名字即可。
// p == path int execlp(const char *file, const char *arg, ...);
- 参数:
-
file
: 可执行程序的名字- 在环境变量PATH中,可执行程序可以不加路径
- 没有在环境变量PATH中, 可执行程序需要指定绝对路径
-
arg
: ps aux 查看进程的时候, 启动的进程的名字, 可以随意指定, 一般和要启动的可执行程序名相同 -
...
: 要执行的命令需要的参数,可以写多个,最后以 NULL 结尾,表示参数指定完了。
-
- 返回值:如果这个函数执行成功, 没有返回值,如果执行失败, 返回 -1
3. 函数的使用
一般不会在进程中直接调用,如果直接调用这个进程的代码区代码被替换也就不能按照原来的流程工作了。
一般在调用这些函数的时候都会先创建一个子进程,在子进程中调用 exec 族函数,子进程的用户区数据被替换掉开始执行新的程序中的代码逻辑,但是父进程不受任何影响仍然可以继续正常工作。
execl() 或者 execlp() 函数的使用方法如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main() { // 创建子进程 pid_t pid = fork(); // 在子进程中执行磁盘上的可执行程序 if(pid == 0) { // 磁盘上的可执行程序 /bin/ps #if 1 execl("/bin/ps", "title", "aux", NULL); // 也可以这么写 // execl("/bin/ps", "title", "a", "u", "x", NULL); #else execlp("ps", "title", "aux", NULL); // 也可以这么写 // execl("ps", "title", "a", "u", "x", NULL); #endif // 如果成功,当前子进程的代码区被ps中的代码区代码替换 // 下面的所有代码都不会执行 // 如果函数调用失败了,才会继续执行下面的代码 perror("execl"); printf("++++++++++++++++++++++++\n"); printf("++++++++++++++++++++++++\n"); printf("++++++++++++++++++++++++\n"); printf("++++++++++++++++++++++++\n"); printf("++++++++++++++++++++++++\n"); printf("++++++++++++++++++++++++\n"); } else if(pid > 0) { printf("我是父进程.....\n"); } return 0; }