一、替换原理:
用fork创建子进程后执行的是和父进程相同的程序,子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
二、替换函数:
#include<unistd.h>
1、int execl(const char *path//路径,const char *arg//命令怎么输就怎么填,...必须以NULL结束);
2、int execlp(const char *file,const char *arg,...);
3、int execle(const char *path,const char *arg,...char *const envp[]);
4、int execv(const char *path,char*const argv[]);
5、int execvp(const char *file,char *const argv[]);
6、int execve(const char *path,char *const argv[],char *const envp[]);
1、int execl(const char *path//路径,const char *arg//命令怎么输就怎么填,…必须以NULL结束);
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 int main()
6 {
7 pid_t id=fork();
8 if(id==0){//child
9 sleep(1);
10 execl("/bin/ls","ls","-a","-l",NULL);
11 exit(0);
12 }else if(id>0){//father
13 pid_t ret=wait(NULL);
14 printf("wait child success!: %d\n",ret);
15
16 }
17 return 0;
18 }
程序运行:
系统命令:
结论:运用execl函数模拟实现了ls -a -l的命令,完成了进程替换。
2、int execlp(const char *file,const char *arg,…);
execlp("ls","ls","-a","-l",NULL);
结论:效果和exec一样。
3、int execv(const char *path,char*const argv[]);
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 int main()
6 {
7 pid_t id=fork();
8 if(id==0){//child
9 sleep(1);
10 // execl("/bin/ls","ls","-a","-l",NULL);
11 // execlp("ls","ls","-a","-l",NULL);
12 char *myargv[]={
13 "ls","-a","-l","-n",NULL,
14 };
15 execv("/bin/ls",myargv);
16 exit(0);
17 }else if(id>0){//father
18 pid_t ret=wait(NULL);
19 printf("wait child success!: %d\n",ret);
20
21 }
22 return 0;
23 }
结论:
运用execvp函数模拟实现了ls -a -l -n的命令,完成了进程替换。
4、int execvp(const char *file,char *const argv[]);
execvp("ls",myargv);
结论:
execvp与execv运行结果一样
5、int execle(const char *path,const char *arg,…char *const envp[]);
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 int main()
6 {
7 pid_t id=fork();
8 if(id==0){//child
9 sleep(1);
10 // execl("/bin/ls","ls","-a","-l",NULL);
11 // execlp("ls","ls","-a","-l",NULL);
12 char *myargv[]={
13 "ls","-a","-l","-n",NULL,
14 };
15 char *myenv[]={
16 "MYPATH=/home/ad/code/30class/day57_进程替换/a.out",
17 NULL,
18 };
19 execle("/home/ad/code/30class/day57_进程替换/a.out","a.out",NULL,myenv);
20 // execv("/bin/ls",myargv);
21 // execvp("ls",myargv);
22 exit(0);
23 }else if(id>0){//father
24 pid_t ret=wait(NULL);
25 printf("wait child success!: %d\n",ret);
26
27 }
28 return 0;
29 }
结论:在execle.c里打印了一长串hello,world!,生成可执行程序./a.out,用execle函数将a.out一替换,子进程就执行了a.out的功能。
6、int excve(const char *path,char *const argv[],char *const envp[]);
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 int main()
6 {
7 pid_t id=fork();
8 if(id==0){//child
9 sleep(1);
10 // execl("/bin/ls","ls","-a","-l",NULL);
11 // execlp("ls","ls","-a","-l",NULL);
12 char *myargv[]={
13 "ls","-a","-l",NULL,
14 };
15 char *myenv[]={
16 "MYPATH=/home/ad/code/30class/day57_进程替换/a.out",
17 NULL,
18 };
19 // execle("/home/ad/code/30class/day57_进程替换/a.out","a.out",NULL,myenv);
20 execve("/bin/ls",myargv,myenv);
21 // execvp("ls",myargv);
22 // execve("/home/ad/code/30class/day57_进程/a.out ","a.out",NULL,myenv);
23 exit(0);
24 }else if(id>0){//father
25 pid_t ret=wait(NULL);
26 printf("wait child success!: %d\n",ret);
27
28 }
29 return 0;
30 }
程序运行:
命令执行:
结论:
观察运行结果发现两者实现的功能基本一致。
三、函数解释:
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
- 如果调用出错则返回-1
- exec函数只有出错的返回值而没有成功的返回值。
四、命名理解:(为了便于记忆)
- l(list):表示参数采用列表
- v(vector):参数用数组
- p(path):有p自动搜索环境变量PATH
- e(env):表示自己维护环境变量
五、
六、
事实上,只有execv是真正的系统调用,其他五个函数最终都调用execve,他们的关系如下图:
七、自行实现一个Shell
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<ctype.h>
5 #include<sys/wait.h>
6 #include<string.h>
7 void do_exec(char argc,char **argv)
8 {
9 pid_t pid=fork();
10 if(pid==0){
11 execvp(argv[0],argv);
12 perror("execvp");
13 }
14 wait(NULL);
15 }
16 void do_parse(char *buf)
17 {
18 char*argv[8]={};
19 int argc=0;
20 int i;
21 int status=0;
22 for(i=0;buf[i]!=0;i++)
23 {
24 if(status==0&&!isspace(buf[i]))//判断是否为空格
25 {
26 argv[argc++]=buf+i;
27 status=1;
28 }
29 else if(isspace(buf[i]))
30 {
31 status=0;
32 buf[i]=0;
33 }
34 }
35 do_exec(argc,argv);//argv第一个参数为ELF,第二个> 参数为命令,以NULL结束
36 }
37 int main()
38 {
39 char buf[1024]={};
40 while(1){
41 printf("my shell>");
42 memset(buf,0x00,sizeof(buf));
43 while(scanf("%[^\n]%*c",buf)==0) {
44 //表示读入一行字符串。
45 //^表示"非"[^\n]表示读入换行字符就结束读入
46 //*表示该输入项读入后不赋予任何变量,即
47 //scanf("%*[^\n]%*c")表示跳过一行字符串。
48
49 printf("myshell>");
50 while (getchar()=='\n');
51 }
52 do_parse(buf);
53
54 }
55 }
运行效果:
结论:简易实现了一个小型shell,运用函数调用替换了命令执行的效果。