前言
父进程创建子进程去执行任务,执行的代码和数据,其实都是父进程的一部分,如果我们想让子进程执行全新的代码和数据,不再和父进程有任何关系怎么办?——进程程序替换,本文我们就来聊一聊进程程序替换;
程序替换的基本操作
用fork创建子进程后执行的是父进程相同的程序,可以通过调用一种 exec 函数替换原来的程序,以执行另一个程序;
程序替换的接口
常用的exec函数有六种:
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[]*/);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
注意:
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
- 如果调用出错则返回-1
- 所以exec函数只有出错的返回值而没有成功的返回值
这些函数看似很多,很复杂,但掌握了规律,使用和记忆就会简单许多;
- l(list) : 表示参数采用列表
- v(vector) : 参数用数组
- p(path) : 有p自动搜索环境变量PATH
- e(env) : 表示自己维护环境变量
示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
extern char** environ;
int main()
{
char* const myenv[] = {
"MYVAL1=11111111111111",
"MYVAL2=11111111111111",
"MYVAL3=11111111111111",
NULL
};
pid_t id = fork();
if (id == 0)
{
char* const argv[] =
{
"ls",
"-a",
"-l",
NULL
};
printf("pid: %d,exec command begin\n", getpid());
// 不带p需要指定路径
//execl("./mytest","mytest",NULL);
//execl("/usr/bin/ls","ls","-a","-l",NULL);
//execl("/usr/bin/python3","python3","test.py",NULL);//调用python程序
// 带 e 允许调用时直接指定新程序的环境变量列表
execle("./mytest", "mytest", "-a", "-b", NULL, myenv);
//execle("./mytest", "mytest", "-a", "-b", NULL, environ);
// 带p无需指定路径,默认从环境变量PATH中找
//execlp("ls","ls","-a","-l",NULL);
// 带 v 可以理解为以数组的方式传参
//execv("/usr/bin/ls", argv);
//execvp("ls", argv);
printf("pid: %d,exec command end\n", getpid());
exit(1);
}
else {
// father
pid_t rid = waitpid(-1, NULL, 0);
if (rid > 0)
{
printf("wait success, rid: %d\n", rid);
}
}
return 0;
}
事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve
这些函数之间的关系:
程序替换的原理
exec函数会将原进程的代码和数据进行替换(不会产生新的进程,进程PID不会变);
注意:
进行程序替换时,并不会替换掉子进程的环境变量,替换的新程序依然默认使用从父进程继承下来的环境变量;
如果子进程想用新的环境变量,可以在程序替换时进行传递,比如:execle;带e的exec函数可以传递环境变量,调用者可以自定义环境变量表进行传递,但需要注意的是,这里传递的环境变量是覆盖式的,而不是拼接式的;
思考:
父进程创建子进程,子进程与父进程用同一份代码,那程序替换后会不会影响父进程?
不会,程序替换时也会对代码进行写时拷贝;
程序替换后,新的代码和数据加载到内存中,那么子进程如何知道替换进来的程序从哪里执行?
执行到哪里?
如何知道执行到哪里:程序计数器(pc),是粗、CPU内部的寄存器(eip),调用哪个程序就把程序的entry(程序的入口)填到CPU内部的eip寄存器中;而程序被调度切换时,就会把寄存器中的数据(进程上下文)保存到进程的PCB中(间接保存),其中就包含eip的数据,等下次调度执行时,先加载进程上下文,这样CPU就知道了进程的执行情况;
结语
以上便是本文的全部内容,希望对你有所帮助或启发,最后,感谢阅读!