文章目录
(一)进程替换
- 进程替换不会创建新的进程,进程PCB未发生改变,进程实体(数据代码内容)被替换
- 进程替换成功后不会执行替换函数下的代码,失败后会执行
- 进程替换成功不返回,失败后返回-1
(二)环境变量
- 环境变量的作用:
当使用shell来运行一个程序时,若没有加绝对路径,系统先会在当前路径下寻找该程序,若没找到就会去环境变量中去寻找该程序。都没找到就会报没有该指令的错误。
jiege@ubuntu:~/Desktop/code/c$ pwd
/home/jiege/Desktop/code/c
jiege@ubuntu:~/Desktop/code/c$ which gcc
/usr/bin/gcc
jiege@ubuntu:~/Desktop/code/c$ echo $PATH
/home/jiege/tools/nodejs/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
jiege@ubuntu:~/Desktop/code/c$
- 查看所有的环境变量
#include <stdio.h>
int main(int arg, char* argv[], char* envp[])
{
int i = 0;
while(envp[i] != NULL)
{
printf("%s\n", envp[i++]);
}
return 0;
}
结果:
(三)进程替换API(unistd.h)
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
- l : 使用参数列表
- p:使用文件名,并从PATH环境进行寻找可执行文件
- v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
- e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
(1)系统调用API
#include <unistd.h>
int execve(const char *pathname, char *const argv[],
char *const envp[]);
(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);
#include <stdio.h> #include <unistd.h> int main() { printf("hello\n"); execlp("ls", "ls", "-a", "-l", (char*)NULL); printf("world\n"); return 0; }
- 结果:
jiege@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输出未设置环境变量的变量
#include <stdio.h> #include <stdlib.h> int main() { printf("myenvp: %s\n", getenv("MYENVP")); return 0; }
- 结果
- 程序b再使用进程替换给a程序传入环境变量
#include <stdio.h> #include <unistd.h> 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指针数组中
- 例:
#include <stdio.h> #include <unistd.h> 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
#include <stdio.h> #include <unistd.h> int main() { printf("A start execvp\n"); char* argv[] = { "hello", "world", NULL}; execvp("./b", argv); perror("execvp err"); return 0; }
-
b.c
#include <stdio.h> #include <unistd.h> 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
指令,查看当前进程的完整格式
jiege@ubuntu:~$ ps -f
UID PID PPID C STIME TTY TIME CMD
jiege 2680 2669 0 14:28 pts/0 00:00:00 bash
jiege 11269 2680 0 17:07 pts/0 00:00:00 ps -f
jiege@ubuntu:~$
-
分析:
可以看到
ps -f
这个进程的进程号PID是11269,它的父进程的进程号PPID是2680;而2680就是bash这个进程。 -
结论:
bash就是这个shell窗口的进程的名字,当你输入
ps -f
时,bash这个进程就fork一个子进程,子进程进程替换执行ps -f命令,将此时的结果输出给父进程bash,父进程输出打印结果
(五)进程替换测试
- 代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
printf("pid of main: %d\n", getpid());
//进程替换
execl("/bin/ps", "ps", "-f", (char*)NULL);
perror("exec err");
exit(0);
}
- 执行结果
jiege@ubuntu:~/Desktop/code/exec$ ./main
pid of main: 11556
UID PID PPID C STIME TTY TIME CMD
jiege 9970 2669 0 14:28 pts/1 00:00:00 bash
jiege 11556 9970 0 17:50 pts/1 00:00:00 ps -f
jiege@ubuntu:~/Desktop/code/exec$
-
结果分析:
- 在bash中执行./main时,bash进程fork出子进程,子进程替换成main程序,main程序中再进行进程替换成ps -f程序
(六)进程替换API总结
函数名 | 参数传递形式 | 路径 | 是否导入环境变量 |
---|---|---|---|
execl | 列表 | 需要可执行程序路径 | 不导入 使用当前环境变量 |
execlp | 列表 | 默认在环境变量中找 | 不导入 使用当前环境变量 |
execle | 列表 | 需要可执行程序路径 | 导入 使用导入的环境变量 |
execv | 数组 | 需要可执行程序路径 | 不导入 使用当前环境变量 |
execvp | 数组 | 默认在环境变量中找 | 不导入 使用当前环境变量 |
execve | 数组 | 需要可执行程序路径 | 导入 使用导入的环境变量 |