0. 引言
进程是一个正在运行的程序,每一个进程都对应着一个 PCB (进程控制块),如图
进程替换操作不再重新产生新的 PCB,而是用新的进程替换掉旧的进程,只对原来的 PCB 做部分修改,如图
1. exec 系列函数
exec 系列函数总共7个,分为三组
#include <unistd.h>
//第一组,l->list,p->path,e->envp
int execl(const char *pathname, const char *arg, ..., (char*)NULL);
int execlp(const char *file, const char *arg, ..., (char*)NULL);
int execle(const char *pathname, const char *arg, ..., (char*)NULL, char *const envp[]);
//第二组,v->vector,p->path,e->envp
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
//第三组,v->vector,e->envp
int execve(const char *pathname, char *const argv[], char *const envp[]);
参数介绍
- pathname:新替换的程序的路径+名称
- file:新替换的程序的名称
- arg:传给新程序主函数的第一个参数,一般为程序名称,后面是剩余参数列表,参数个数可变,必须以空指针作为最后一个参数
- argv:传给新程序主函数的参数数组,把所有的arg写到一个数组中
- envp:传给新程序主函数的环境变量
其中,前两组属于库函数,第三组属于系统调用,前两组函数的内部实现都是调用了最后一个函数。以 ps 替换当前进程为例,通过命令 which ps 查看 ps 命令的路径为 /usr/bin/ps,execl 的代码示例如下
//execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main(){
printf("main pid=%d\n", getpid());
//execl执行成功不返回,直接从新程序的主函数开始执行,只有失败才会返回错误码
execl("/usr/bin/ps", "ps", "-fL", (char*)0);
perror("exec error");
exit(0);
}
程序输出如下
可见,进程替换后新进程(ps)继承了原来进程的 pid。
//上面代码中的语句
execl("/usr/bin/ps", "ps", "-fL", (char*)0);
//可以用其他几个函数代替
//execlp
execlp("ps", "ps", "-fL", (char*)0);
//execle
char *myenvp[] = {"MYSTR=hello", "VAL=100", (char*)0};
execle("/usr/bin/ps", "ps", "-fL", (char*)0, myenvp);
//execv
char *myargv[] = {"ps", "-fL", (char*)0};
execv("/usr/bin/ps", myargv);
//execvp
char *myargv[] = {"ps", "-fL", (char*)0};
execvp("ps", myargv);
//execvpe
char *myargv[] = {"ps", "-fL", (char*)0};
char *myenvp[] = {"MYSTR=hello", "VAL=100", (char*)0};
execvpe("ps", myargv, myenvp);
//execve
char *myargv[] = {"ps", "-fL", (char*)0};
char *myenvp[] = {"MYSTR=hello", "VAL=100", (char*)0};
execve("/usr/bin/ps", myargv, myenvp);
2. fork 与 exec 配合使用创建全新进程
主程序 a 产生一个子进程,子进程用新程序 b 替换
//a.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <assert.h>
#include <errno.h>
int main(){
printf("main pid=%d\n", getpid());
pid_t pid = fork();
assert(pid != -1);
if(pid == 0){
char *myargv[] = {"b", "hello", "world", (char*)0};
char *myenvp[] = {"MYSTR=nihao", "VAL=100", (char*)0};
execve("./b", myargv, myenvp);
perror("exec error");
exit(0);
}
wait(NULL);
printf("main over\n");
exit(0);
}
//b.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main(int argc, char *argv[], char *envp[]){
printf("b pid=%d\n", getpid());
printf("argc=%d\n", argc);
for(int i = 0; i < argc; i++){
printf("argv[%d]=%s\n", i, argv[i]);
}
for(int i = 0; envp[i] != NULL; i++){
printf("envp[%d]=%s\n", i, envp[i]);
}
exit(0);
}
程序输出结果如下
可以看出,程序 a 的 pid=249556,运行过程中产生了 pid=249557 的子进程,该子进程被程序 b 替换并继承了 pid。同时,程序 b 也接收到了从程序 a 中传来的参数 myargv 和 myenvp,当程序 b 运行结束将返回码传给程序 a,程序 a 继续执行直至结束。