一、进程体替换(exec函数族)
使用函数fork()创建新的子进程后,子进程往往需要调用函数exec()以执行另一个程序。当进程调用函数exec()时,该进程执行的程序完全替换为新程序,而新程序则从其函数main()开始执行。
与 fork 或 vfork 函数不同,exec 函数不是创建调用进程的子进程,而是创建一个新的进程取代调用进程自身。新进程会用自己的全部地址空间,覆盖调用进程的地址空间,但进程的 PID 保持不变。exec 只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。
exec函数族的作用:根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
#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 *path, char *const argv[], char *const envp[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
// 7 个函数返回值:若出错,返回 -1;若成功,不返回
1、execl函数
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
若出错,返回 -1, 若成功,不返回
execl()其中后缀 “l” 代表 list 也就是参数列表的意思,第一参数 path 字符指针所指向要执行的文件路径, 接下来的参数代表执行该文件时传递的参数列表:argv[0],argv[1]… 最后一个参数须用空指针NULL作结束。
#include <unistd.h>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
cout << "hello\n";
// 去磁盘中加载另外一段程序,替换掉当前的正文段、数据段等。
// 11 22 是传给./test(可执行程序)的参数。
execl("test", "./test", "11", "22", NULL);
cout << "world\n";
return 0;
}
2、execlp函数
#include <unistd.h>
int execlp(const char *file, const char *arg, ...);
若出错,返回 -1, 若成功,不返回
execlp()会从 PATH 环境变量所指的目录中查找符合参数 file 的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid = fork();
if (pid == 0) // 子进程启动火狐,打开一个网址
{
// NULL表示参数结束
execlp("/usr/bin/firefox", "firefox", "www.baidu.com", NULL);
}
else if (pid > 0)
{
while (1)
{
printf("I am parent\n");
sleep(1);
}
}
else
{
perror("fork");
exit(1);
}
return 0;
}
当指定 file 作为参数时,如果 file 中包含 /,则就将其视为路径名。否则就按 PATH 环境变量,在它所指定的各目录中搜索可执行文件。这也就是 execlp 和 execl 的区别了。
二、调用命令行(system)
#include <stdlib.h>
int system(const char *command);
1、函数功能与参数解析
linux系统中可以使用函数system()函数调用shell命令。
system()函数执行 command 参数表示的命令行,并返回命令进程的终止状态。若 command 参数是一个空指针 (NULL),则仅当命令处理程序可用时,system 返回非 0 值,这一特征可以确定在一个给定的操作系统上是否支持 system 函数。在 UNIX 中,system 总是可用的。
2、函数详解
因为 system 在其实现中调用了 fork、exec 和 waitpid,因此有 3 种返回值。
- fork 失败或者 waitpid 返回除 EINTR 之外的出错,则 system 返回 -1,并且设置 errno以指示错误类型。
- 如果 exec 失败(表示不能执行 shell),则其返回值如同 shell 执行了 exit (127) 一样。
- 如果所有 3 个函数(fork、exec 和 waitpid)都成功,那么 system 的返回值是 shell 的终止状态,其格式已在 waitpid 中说明。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int status;
// 若 command 参数是一个空指针 (NULL),
// 则仅当命令处理程序可用时,system 返回非 0 值。
if ((status = system(NULL)) == -1)
perror("system"), exit(1);
// 通过 if (!status) 判断系统是否支持 system 函数。
if (!status)
printf("shell 不可用\n");
else
{
// 如果成功,那么 system 的返回值是 shell 的终止状态
// 如果将shell命令错误则,终止状态为 exit(127)
if ((status = system("ls -l")) == -1)
perror("system"), exit(1);
// WEXITSTATUS (status) 得到 exit或_exit 参数、return返回值。
printf("WEXITSTATUS (status) = %d\n", WEXITSTATUS(status));
}
return 0;
}
3、system与exec的区别
使用 system 函数而不直接使用 fork+exec 的好处是,syetem函数针对各种错误和信号都做了必要的处理,而且 system 是标准库函数,可跨平台使用。
【与exec的区别】:
1. system()和exec()都可以执行进程外的命令,system是在原进程上开辟了一个新的进程,但是exec是用新进程(命令)覆盖了原有的进程。
2. system()和exec()都有能产生返回值,system的返回值并不影响原有进程,但是exec的返回值影响了原进程。
参考:https://blog.csdn.net/qq_29350001/article/details/70837744