本文主要讲解以下几个知识点:
1.进程等待
2.进程程序替换
3.实现一个简单的shell,并且认识shell的运行原理
一.进程等待
1.首先让我们搞清楚为什么需要进程等待??
我们知道僵尸进程是一个很可怕的东西,就算用kill -9都是干不掉的,而且僵尸进程就像是毒瘤一般会造成很大的危害(内存泄漏)。所以进程等待很大程度上就是为了避免僵尸进程的产生
2.进程等待的必要性
- 为了避免产生僵尸进程,造成危害
- 父进程派给子进程的任务完成的如何,我们需要知道。例如:子进程退出后,运行的结果对还是不对,或者是否是正常退出
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
3.进程等待的方法
a)wait方法
wait()函数是等待任意子进程退出(阻塞式等待函数)
阻塞式调用:如果没有子进程退出,就一直等待,不返回,直到子进程退出
代码演示:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<errno.h>
4 #include<stdlib.h>
5 #include<wait.h>
6 int main()
7 {
8 pid_t pid;
9
10 pid = fork();
11 if(pid < 0)
12 {
13 perror("fork");
14 exit(1);
15 }
16 else if(pid == 0)
17 {
18 sleep(10);
19 exit(10);
20 }
21 else
22 {
23 int st;
24 int ret = wait(&st);
25 if(ret > 0 && (st & 0X7E) == 0)
26 {
//正常退出
27 printf("child exit code:%d\n",(st>>8)&0XFF);
29 }
30 else if(ret > 0)
31 {
异常退出
32 printf("sig code:%d\n",st & 0X7E);
33 }
34
35 }
36 return 0;
37 }
我们可看到sleep 10s后会进程正常退出
在这一由于xshell出了点问题没法演示异常退出,下次补上。大家可以在另一个终端执行kill命令来查看异常退出(这里自行查看)
b)waitpid方法
可进行man手册进程查看
代码展示:
阻塞式等待:如果没有子进程退出,就一直等待,不返回,直到子进程退出
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<errno.h>
4 #include<stdlib.h>
5 #include<wait.h>
6 int main()
7 {
8 pid_t pid;
9
10 pid = fork();
11 if(pid < 0)
12 {
13 perror("fork");
14 exit(1);
15 }
16 else if(pid == 0)
17 {
18 printf("child is run,pid is: %d\n",getpid());
19 sleep(10);
20 exit(5);
21 }
22 else
23 {
24 int st = 0;
25 int ret = waitpid(-1,&st,0); //阻塞式等待
26 printf("this is test for wait\n");
27 if(WIFEXITED(st) && ret == pid)
28 {
29 printf("wait child success,return code is: %d\n",WEXITSTATUS(st));
30 }
31 else
32 {
33
34 printf("wait child falied,return \n");
35
36 }
37 }
38 return 0;
39 }
非阻塞式等待:在调用没有在规定时间内接收到返回值时,立即返回。
5 #include<stdio.h>
6 #include<unistd.h>
7 #include<wait.h>
8 #include<errno.h>
9 #include<stdlib.h>
10
11 int main()
12 {
13 pid_t pid = fork();
14
15
16 if(pid < 0)
17 {
18
19 perror("fork");
20 return -1;
21 }
22
23 else if(pid == 0)
24 {
25 printf("child!=> %d\n",getpid());
26 sleep(5);
27 exit(-1);
28 }
29 else
30 {
31 int status = 0;
32 int ret = 0;
33 do
34 {
35
36 ret = waitpid(-1,&status,WNOHANG);
37 if(ret == 0)
38 {
39 printf("child is run!!\n");
40
41 }
42
43 sleep(2);
44 }while(ret == 0);
45 if(ret == pid && WIFEXITED(status))
46 {
47
48 printf("success!! childexit=> %d\n",WEXITSTATUS(status));
49 printf("childpid!!=> %d\n",ret);
50 }
51
52 else
53 {
54 printf("failed!!\n");
55 }
56 printf("father!=> %d ret=> %d\n",getpid(),ret);
57 }
58
59 return 0;
60 }
注意:
- 如果子进程已经退出,再调用wait/waitpid时,则wait/waitpid会立即退出,并且释放资源,获取子进程退出信息
- 如果在任意时刻调用wait/waitpid时,而且子进程存在且正常运行,则进程可能阻塞
- 如果不存在子进程则立刻报错返回
获取子进程status(进程的退出状态码):
- wait/waitpid,都是一个status参数,该参数是一个输出型参数,由操作系统填充
-
在wait/waitpid的参数中存储了子进程的退出原因以及退出码,虽然退出状态用了四个字节来获取,但是参数中只用了低16位两个字节用于存储这些信息
-
在低16位中的高8位中存储退出码,程序运行完毕才会有,低8位为0
-
在低16位中的低7位中存储的是引起异常退出的信号值,当程序异常退出时,状态码是0,高八位为0,第八位存储core dump标志
二.进程替换
1.为什么要进行进程替换??
因为我们在创建进程时,我们希望子进程去做和父进程不同的事,所以就需要进程替换
2.进程替换的特性:
- 再调用exec替换函数后,子进程的用户空间和数据将被新的程序或者变量替换(不用开辟新的空间)
- 在调用exec函数前后,进程ID不变
- 在调用完exec函数后,进程将从新的入口函数执行新的程序
五个替换函数的理解请看博客:
https://blog.youkuaiyun.com/alidada_blog/article/details/82656228
三.实现简单的xshell
实现的功能:
1.获取命令行
2.解析命令行
3.建立一个子进程(fork)
4.替换子进程(execvp)
5.父进程等待子进程退出(wait)
代码展示:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<errno.h>
5 #include<string.h>
6 #include<ctype.h>
7
8 char *argv[8];
9
10 int argc = 0;
11 void do_parse(char* buf)
12 {
13 int status = 0;
14 int i = 0;
15 for(argc = i = 0;buf[i];i++)
16 {
17 /*
18 *isspace检测空格的函数,如果有空格则输出ture,不是空格返回0
19 *
20 *
21 * */
22
23 if( !isspace(buf[i]) && status == 0)
24 {
25 argv[argc++] = buf + i;
26
27 status = 1;
28
29
30 }
31 else if(isspace(buf[i]))
32 {
33 buf[i] = 0;
34 status = 0;
35
36 }
37
38 }
39
40 argv[argc] = NULL;
41 }
42
43
44 //对进程的操作函数
45 void do_execute()
46 {
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<errno.h>
5 #include<string.h>
6 #include<ctype.h>
7
8 char *argv[8];
9
10 int argc = 0;
11 void do_parse(char* buf)
12 {
13 int status = 0;
14 int i = 0;
15 for(argc = i = 0;buf[i];i++)
16 {
17 /*
18 *isspace检测空格的函数,如果有空格则输出ture,不是空格返回0
19 *
20 *
21 * */
22
23 if( !isspace(buf[i]) && status == 0)
24 {
25 argv[argc++] = buf + i;
26
27 status = 1;
28
29
30 }
31 else if(isspace(buf[i]))
32 {
33 buf[i] = 0;
34 status = 0;
35
36 }
37
38 }
39
40 argv[argc] = NULL;
41 }
42
43
44 //对进程的操作函数
45 void do_execute()
46 {
47 pid_t pid = fork();
48
49 if(pid < 0)
50 {
51 perror("fork");
52 exit(0);
53 }
54 else if(pid == 0)
55 {
56 execvp(argv[0],argv);
57 perror("execvp");
58 exit(0);
59 }
60 else
61 {
62 int st;
63 int ret = wait(&st);
64 while(ret != pid);
65
66
67 }
68
69
70 }
71
72
73 int main()
74 {
75
76 char buf[1024] = {};
77 while(1)
78 {
79 printf("myshell=> ");
80 scanf("%[^\n]%*c",buf);
81
82 do_parse(buf);
83 do_execute();
84 }
85
86 return 0;
87 }