【Linux】进程替换
(一)进程替换
- 进程替换不会创建新的进程,进程PCB未发生改变,进程实体(数据代码内容)被替换
- 进程替换成功后不会执行替换函数下的代码,失败后会执行
- 进程替换成功不返回,失败后返回-1
(二)环境变量
- 环境变量的作用:
当使用shell来运行一个程序时,若没有加绝对路径,系统先会在当前路径下寻找该程序,若没找到就会去环境变量中去寻找该程序。都没找到就会报没有该指令的错误。
1 | jiege@ubuntu:~/Desktop/code/c$ pwd |
- 查看所有的环境变量
1 |
|
结果:
(三)进程替换API(unistd.h)
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
- l : 使用参数列表
- p:使用文件名,并从PATH环境进行寻找可执行文件
- v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
- e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
(1)系统调用API
1 |
|
(2)库函数API
int execl(const char* pathname, const char* arg, .../*(char*)NULL*/);
参数解释:
目标程序的进程替换
- pathname:目标程序的路径
- arg:执行这个程序的方式
- 返回值:失败-1
- 例如:
execl("/bin/ls", "ls" "-a", "-l", (char*)NULL);
int execlp(const char *file, const char *arg, .../* (char*) NULL */);
参数解释:
该函数会在环境变量的路径中查找file
- file:要执行的目标程序
- arg:传给目标程序的参数
- 返回值:失败-1
- 例如:
execlp("ls", "ls", "-a", "-l", (char*)NULL);
1
2
3
4
5
6
7
8
9
10
11
int main()
{
printf("hello\n");
execlp("ls", "ls", "-a", "-l", (char*)NULL);
printf("world\n");
return 0;
}- 结果:
1
2
3
4
5
6
7
8jiege@ubuntu:~/Desktop/test$ vim main.c
jiege@ubuntu:~/Desktop/test$ gcc -o main main.c
jiege@ubuntu:~/Desktop/test$ ./main
hello
总用量 24
-rwxrwxr-x 1 jiege jiege 16736 12月 9 20:12 main
-rw-rw-r-- 1 jiege jiege 148 12月 9 20:12 main.c
jiege@ubuntu:~/Desktop/test$
int execle(const char *pathname, const char *arg, .../* (char*) NULL, char *const envp[] */);
参数解释:
给这个目标进程传入指定envp的环境变量
pathname:目标程序的路径
arg:替换后如何执行的方式,envp表示要导入的环境变量
例:
- 程序myenv输出未设置环境变量的变量
1
2
3
4
5
6
7
8
int main()
{
printf("myenvp: %s\n", getenv("MYENVP"));
return 0;
}- 结果
- 程序b再使用进程替换给a程序传入环境变量
1
2
3
4
5
6
7
8
9
10
int main()
{
char* buff[] = {"MYENVP=/home/jiege/awei", NULL};
execle("./myenv", "myenv", NULL, buff);
perror("execle err");
return 0;
}- 结果
注意:envp数组前的参数是NULL,envp数组最后一个元素为NULL,并且该操作会覆盖原环境变量的值
int execv(const char *pathname, char *const argv[]);
参数详解:
- pathname:目标程序路径名
/usr/...
- argv:将所有的执行方式存放在argv指针数组中
- 例:
1
2
3
4
5
6
7
8
9
10
11
int main(int argc, char* argv[])
{
char* buff[] = {"ls", "-l", NULL};
execv("/usr/bin/ls", buff);
perror("execv err");
return 0;
}- 结果:
- pathname:目标程序路径名
int execvp(const char *file, char *const argv[]);
参数详解:
给目标程序传入参数
- file:被执行的目标程序
argv:传给file程序的参数
例:使用程序a中替换成程序b,b将得到的参数打印
a.c
1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
printf("A start execvp\n");
char* argv[] = {"hello", "world", NULL};
execvp("./b", argv);
perror("execvp err");
return 0;
}
b.c
1
2
3
4
5
6
7
8
9
10
11
12
int main(int argc, char* argv[])
{
int i = 0;
while(argv[i] != NULL)
{
printf("%s\n", argv[i++]);
}
return 0;
}
- 结果:
int execvpe(const char *file, char *const argv[], char *const envp[]);
参数详解:
- file:目标程序
- argv:函数参数
- envp:环境变量
(四)进程替换和fork的结合使用案例
- 一个例子:就比如bash窗口中输入
ps -f
指令,查看当前进程的完整格式
1 | jiege@ubuntu:~$ ps -f |
分析:
可以看到
ps -f
这个进程的进程号PID是11269,它的父进程的进程号PPID是2680;而2680就是bash这个进程。结论:
bash就是这个shell窗口的进程的名字,当你输入
ps -f
时,bash这个进程就fork一个子进程,子进程进程替换执行ps -f命令,将此时的结果输出给父进程bash,父进程输出打印结果
(五)进程替换测试
- 代码
1 |
|
- 执行结果
1 | jiege@ubuntu:~/Desktop/code/exec$ ./main |
结果分析:
- 在bash中执行./main时,bash进程fork出子进程,子进程替换成main程序,main程序中再进行进程替换成ps -f程序
(六)进程替换API总结
函数名 | 参数传递形式 | 路径 | 是否导入环境变量 |
---|---|---|---|
execl | 列表 | 需要可执行程序路径 | 不导入 使用当前环境变量 |
execlp | 列表 | 默认在环境变量中找 | 不导入 使用当前环境变量 |
execle | 列表 | 需要可执行程序路径 | 导入 使用导入的环境变量 |
execv | 数组 | 需要可执行程序路径 | 不导入 使用当前环境变量 |
execvp | 数组 | 默认在环境变量中找 | 不导入 使用当前环境变量 |
execve | 数组 | 需要可执行程序路径 | 导入 使用导入的环境变量 |