父进程创建子进程的目的:
1.想让子进程执行父进程代码的一部分。(子承父业)
2.想让子进程执行一个全新的程序。
进程程序替换讨论的就是子进程执行一个全新程序的情况。
进程替换主要是通过调用库函数中众多exec类函数来实现的。
首先让我们来看看其中最简单的
#include
int execl(const char *path, const char *arg, ...);
将指定的程序加载到内存中,让指定进程进行执行。
其中
path:是如何找到程序的路径
arg:是可变参数列表:arg[0]、arg[1]、arg[2],最后一个跟NULL结束
返回值:函数调用失败返回-1,成功的返回值没有意义。
如:
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main()
5 {
6 printf("process is running...\n");
7 execl("/usr/bin/ls", "ls", "--color=auto", "-a", "-l", NULL);
8 printf("process running done...\n");
9
10 return 0;
11 }
只允许第一行打印,随后执行对应命令,进程代码被替换最后一行不打印,结果没错。
进程替换原理也其实很简单,就是将原来进程数据代码内容给替换了
下面对应进程替换函数都属于前缀exec的
首先看exec中列表类的,命名理解:
#include
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[]);
execlp只需要修改路径为程序名。
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main()
5 {
6 printf("process is running...\n");
7 execlp("ls", "ls", "--color=auto", "-a", "-l", NULL);
8 printf("process running done...\n");
9
10 return 0;
11 }
execle需要自己指定环境变量,如果不写默认继承进程的环境变量。
//mybin.c中
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 int main()
5 {
6 printf("PATH:%s\n", getenv("PATH"));
7 printf("PWD:%s\n", getenv("PWD"));
8 printf("MYENV:%s\n", getenv("MYENV"));
9 return 0;
10 }
//myexe.c中
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main()
5 {
6 char* const envp_[] = {(char*)"MYENV=12235", NULL};
7 execle("./mybin", "mybin", NULL, envp_);
8 return 0;
9 }
可以看到,这里替换成了自己写的mybin程序,并且指定了自己组装envp_环境变量。
除了列表类的,还是数组类的。
exec中数组类的,命名理解:
#include
int execv(const char *path, char* const argv[]);
int execvp(const char *file, char* const argv[]);
int execvpe(const char *path, char* const argv[], char* const envp[]);
例子:
//mybin.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 int main(int argc, char* argv[])
6 {
7 printf("PATH:%s\n", getenv("PATH"));
8 printf("PWD:%s\n", getenv("PWD"));
9 printf("MYENV:%s\n", getenv("MYENV"));
10
11 if(strcmp(argv[1], "-t") == 0)
12 {
13 printf("使用了-t\n");
14 }
15 return 0;
16 }
//myexec.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <sys/wait.h>
5 #include <assert.h>
6
7 int main()
8 {
9 printf("process is running...\n");
10 pid_t id = fork();
11 assert(id != -1);
12
13 if(id == 0)
14 {
15 extern char** environ;
16 char* const myargc_[] = {(char*)"mybin", (char*)"-t", NULL}; //自己组建命令行参数
17 execvpe("./mybin", myargc_, environ); //调用继承的环境变量
18 exit(1); //mush failed
19 }
20
21 int status = 0;
22 pid_t ret = waitpid(id, &status, 0);
23 if(ret > 0)
24 {
25 printf("wait success: exit code: %d, sig: %d\n", (status>>8)&0xFF, status & 0x7F);
26 }
27 return 0;
如果我们即想用系统的环境变量,又想用自己的怎么办?
通过putenv(),将定义的环境变量加入到系统中,environ指向的表。
#include
int putenv(char* string);
//通过在之前代码中加入putenv
15 extern char** environ;
16 char* const myargc_[] = {(char*)"mybin", (char*)"-t", NULL};
17 putenv("MYENV=1234");
18 execvpe("./mybin", myargc_, environ);
19 exit(1); //mush failed
通过以上知道了,系统通过exec系列的函数,将硬盘中的程序替换到内存中。程序加载到内存中,这是体系结构规定的,很好理解。
那么回到main函数.
int execve(const char* filename, char* const argv[], char* const envp[]);
int main(int argc, char* argv[], char* env[]);
其实,在main函数被调用之前,先加载了exec系列中的函数,并且main中的参数其实也是由exec函数加载的。
最后一个
通过man 3 exec
库函数手册可以看到这些,但似乎少了一个execve。
再通过man 2 execve
系统手册可以看到它
其实execve才是真正的系统调用,其它的都是通过它的封装,为了给我们满足多种应用场景的。