文章目录
创建一批子进程
cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define N 5
void runChild()
{
int cnt = 10;
while (cnt != 0)
{
printf("i am a child : %d , ppid:%d\n", getpid(), getppid());
sleep(1);
cnt--;
}
}
int main()
{
int i = 0;
for (; i < N; i++)
{
pid_t id = fork();
if (id == 0)
{
//子进程
runChild();
exit(0);
}
//父进程
//父进程继续循环
}
sleep(1000);
return 0;
}
进程终止
进程退出只有三种情况:
代码运行完毕,结果正确。
代码运行完毕,结果不正确
代码异常终止(进程崩溃)
代码异常,本质可能就是代码没有跑完
此时进程的退出码无意义,我们不关心退出码
代码是否正确,统一会采用进程的退出码来进行判断
查看该进程的进程退出码
$?:保存的是最近一次进程退出的时候的退出码
powershell
[cxq@iZ7xviiy0goapxtblgih6oZ lesson15]$ echo $?
查看错误码
errno
cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
int ret = 0;
char *p = (char*)malloc(1000 * 1000 * 1000 * 4);
if (p == NULL)
{
//errno 返回的是错误码 ,strerror可以知道错误原因
printf("malloc error, %d %s\n", errno, strerror(errno));
ret = errno;
}
else
{
printf("malloc success\n");
}
return ret;
}
进程出现异常,本质是我们的进程收到了对应的信号
exit与return的区别
exit在任意地方被调用,都表示调用进程直接退出
return只表示当前函数返回,还会继续向后运行
cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void show()
{
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
exit(13);//exit在任意地方被调用,都表示调用进程直接退出,如果此时换成return,return只表示当前函数返回,还会继续向后运行
printf("end show\n");
printf("end show\n");
printf("end show\n");
printf("end show\n");
printf("end show\n");
}
int main()
{
show();
printf("hello Linux\n");
return 12;
}
cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void show()
{
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
return ;//return只表示当前函数返回,还会继续向后运行
exit(13);//exit在任意地方被调用,都表示调用进程直接退出
printf("end show\n");
printf("end show\n");
printf("end show\n");
printf("end show\n");
printf("end show\n");
}
int main()
{
show();
printf("hello Linux\n");
return 12;
}
_exit :
cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void show()
{
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
printf("hello show\n");
_exit(14);
printf("end show\n");
printf("end show\n");
printf("end show\n");
printf("end show\n");
printf("end show\n");
}
int main()
{
show();
printf("hello Linux\n");
return 12;
}
_exit 与exit的区别
使用exit函数退出进程也是我们常用的方法,exit函数可以在代码中的任何地方退出进程,并且exit函数在退出进程前会做一系列工作
1、执行用户通过atexit或on_exit定义的清理函数。
2、关闭所有打开的流,所有的缓存数据均被写入。
3、调用_exit函数终止进程。
_exit()是系统调用接口,exit()是库函数
当把exit改成_exit时,使用_exit终止进程,不会刷新缓冲区的数据,所以缓冲区当中的数据将不会被输出,就不会看到you can see me 了
但是exit在终止进程之前,会冲刷缓冲区,将缓冲区的数据输出
printf一定是先把数据写入缓冲区中,合适的时候,再进行刷这个缓冲区,缓冲区绝对不在内核中,在用户区
进程等待
进程等待:通过系统调用wait/waitpid,来进行对子进程进行状态检测与回收的功能
1、子进程退出,父进程如果不读取子进程的退出信息,子进程就会变成僵尸进程,进而造成内存泄漏
2、进程一旦变成僵尸进程,那么就算是kill -9命令也无法将其杀死,因为谁也无法杀死一个已经死去的进程
3、对于一个进程来说,最关心自己的就是其父进程,因为父进程需要知道自己派给子进程的任务完成的如何
4、父进程需要通过进程等待的方式,回收子进程资源,获取子进程的退出信息
父进程通过调用wait/waitpid进行僵尸进程的回收问题
监控脚本
powershell
while :; do ps ajx |head -1 && ps ajx | grep testWait | grep -v grep ; sleep 1 ; echo "------------" ; done
wait是等待任意一个子进程退出
如果是多个子进程僵尸,如何利用wait回收?
shell
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 #define N 10
7 void Runchild ()
8 {
9 int cnt =5 ;
10 while(cnt)
11 {
12 printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
13 sleep(1);
14 cnt--;
15 }
16 }
17 int main()
18 {
19 for( int i =0 ; i < N ;i ++ )
20 {
21 pid_t id = fork();
22 if( id ==0 )
23 {
24 //子进程
25 Runchild ();
26 exit(0);
27 }
28 //父进程
29 printf("create child process: %d success\n", id);
30 }
31 sleep(10);
32 //等待
33 for( int i = 0 ; i < N ; i++ )
34 {
35 pid_t id = wait(NULL);
36 if( id>0 )
37 {
38 printf("wait %d success\n", id);
39 }
40
41 }
42
43 sleep(5);
44 return 0 ;
45 }
如果父进程在等待子进程,如果一个子进程或多个子进程不退出 ,父进程会在调用wait时,父进程就一直拿不到子进程的pid , 父进程会一直等待子进程退出,父进程这种状态叫阻塞等待
powershell
#include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 #define N 10
7 void Runchild ()
8 {
9 int cnt =5 ;
10 while(1)
11 {
12 printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
13 sleep(1);
14 cnt--;
15 }
16 }
17 int main()
18 {
19 for( int i =0 ; i < N ;i ++ )
20 {
21 pid_t id = fork();
22 if( id ==0 )
23 {
24 //子进程
25 Runchild ();
26 exit(0);
27 }
28 //父进程
29 printf("create child process: %d success\n", id);
30 }
31 // sleep(10);
32 //等待
33 for( int i = 0 ; i < N ; i++ )
34 {
35 pid_t id = wait(NULL);
36 if( id>0 )
37 {
38 printf("wait %d success\n", id);
39 }
40
41 }
42
43 sleep(5);
44 return 0 ;
45 }
waitpid
等待指定子进程或任意子进程
waitpid的返回值:
1、等待成功返回被等待进程的pid。
2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。
3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。
pid:待等待子进程的pid,若设置为-1,则等待任意子进程
status:输出型参数(通过指针,把函数内部的数据带出来)
获取子进程的退出状态,不关心可设置为NULL
status:操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
cpp
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 #define N 10
7 void Runchild ()
8 {
9 int cnt =5 ;
10 while(1)
11 {
12 printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
13 sleep(1);
14 cnt--;
15 }
16 }
17 int main()
18 {
45 pid_t id = fork() ;
46 if(id <0)
47 {
48 perror("fork");
49 return 1 ;
50 }
51 else if( id == 0 )
52 {
53 //child
54
55 int cnt =5 ;
56 while(cnt)
57 {
58 printf("I am a child ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
59 cnt -- ;
60 sleep(1);
61 }
62 exit(1);
63 }
64 else // parent
65 {
66 int cnt =10 ;
67 while(cnt )
68 {
69 printf("I am a parent ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
70 cnt -- ;
71 sleep(1);
72 }
73 // pid_t ret = wait(NULL);
74
75 int status =0 ;//输出型参数
76 //操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
77 pid_t ret = waitpid( id, &status , 0 );
78
79 //等待成功
80 if( ret ==id ) //id是子进程的pid
81 {
82 //status存放的是子进程的退出信息
83 printf("wait success,ret:%d, status: %d\n", ret, status);
84 }
85 sleep(5);
86
87
88 }
89 return 0 ;
90 }
为什么status是256 ?
在子进程中 ,exit函数里面,我们设置的是1
退出码设置的是1 ,也就是00000001,00000001就是256
子进程退出,一共会有3种退出场景
1、代码运行完毕,结果正确
2、代码运行完毕,结果不正确
3、代码异常终止
父进程等待,需要获得子进程退出的哪些信息?
1、子进程代码是否异常
2、如果没有异常,通过退出码判断来结果是否正确
不同的退出码表示不同的出错原因
status是一个整型变量,但status不能简单的当作整型来看待,status的不同比特位所代表的信息不同,具体细节如下(只研究status低16比特位)
cpp
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 #define N 10
7 void Runchild ()
8 {
9 int cnt =5 ;
10 while(1)
11 {
12 printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
13 sleep(1);
14 cnt--;
15 }
16 }
17 int main()
18 {
45 pid_t id = fork() ;
46 if(id <0)
47 {
48 perror("fork");
49 return 1 ;
50 }
51 else if( id == 0 )
52 {
53 //child
54
55 int cnt =5 ;
56 while(cnt)
57 {
58 printf("I am a child ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
59 cnt -- ;
60 sleep(1);
61 }
62 exit(1);
63 }
64 else // parent
65 {
66 int cnt =10 ;
67 while(cnt )
68 {
69 printf("I am a parent ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
70 cnt -- ;
71 sleep(1);
72 }
73 // pid_t ret = wait(NULL);
74
75 int status =0 ;//输出型参数
76 //操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
77 pid_t ret = waitpid( id, &status , 0 );
78
79 //等待成功
80 if( ret ==id ) //id是子进程的pid
81 {
82 //status存放的是子进程的退出信息
83 //7F 0111 1111
84 //0xFF
85 printf("wait success,ret:%d, exit sig : %d, exit code:%d\n", ret, status&0x7F , (status>>8) &0xFF ); //打印的分别是 id, 信号, 退出码
86 }
87 sleep(5);
88
89
90 }
91 return 0 ;
92 }
通过收到信号判断是否异常
通过看退出码判断是否结果正确
系统当中提供了两个宏来获取退出码和退出信号
WIFEXITED(status):用于查看进程是否是正常退出,本质是检查是否收到信号
WEXITSTATUS(status):用于获取进程的退出码
cpp
exitNormal = WIFEXITED(status); //获取退出信号,判断是否正常退出
exitCode = WEXITSTATUS(status); //获取退出码
例如:
cpp
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 #define N 10
7 void Runchild ()
8 {
9 int cnt =5 ;
10 while(1)
11 {
12 printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
13 sleep(1);
14 cnt--;
15 }
16 }
17 int main()
18 {
45 pid_t id = fork() ;
46 if(id <0)
47 {
48 perror("fork");
49 return 1 ;
50 }
51 else if( id == 0 )
52 {
53 //child
54
55 int cnt =5 ;
56 while(cnt)
57 {
58 printf("I am a child ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
59 cnt -- ;
60 sleep(1);
61 }
62 exit(1);
63 }
64 else // parent
65 {
66 int cnt =10 ;
67 while(cnt )
68 {
69 printf("I am a parent ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
70 cnt -- ;
71 sleep(1);
72 }
73 // pid_t ret = wait(NULL);
74
75 int status =0 ;//输出型参数
76 //操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
77 pid_t ret = waitpid( id, &status , 0 );
78
79 //等待成功
80 if( ret ==id ) //id是子进程的pid
81 {
82 //status存放的是子进程的退出信息
83 //7F 0111 1111
84 //0xFF
85 //printf("wait success,ret:%d, exit sig : %d, exit code:%d\n", ret, status&0x7F , (status>>8) &0xFF ); //打印的分别是 id, 信号, 退出码
if(WIFEXITED(status) )
88 {
89 printf("进程是正常跑完的,退出码:%d\n",WIFEXITED(status));
90 }
91 else
92 {
93 printf("进程出异常了\n");
94 }
86 }
87 sleep(5);
88
89
90 }
91 return 0 ;
92 }
以下代码中同时创建了10个子进程
父进程再使用waitpid函数指定等待这10个子进程
cpp
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 #define N 10
7 void Runchild ()
8 {
9 int cnt =5 ;
10 while(cnt)
11 {
12 printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
13 sleep(1);
14 cnt--;
15 }
16 }
17 int main()
18 {
//创建多进程
19 for( int i =0 ; i < N ;i ++ )
20 {
21 pid_t id = fork();
22 if( id ==0 )
23 {
24 //子进程
25 Runchild ();
26 exit(i);
27 }
28 //父进程
29 printf("create child process: %d success\n", id);
30 }
31 // sleep(10);
32 //多进程等待
33 for( int i = 0 ; i < N ; i++ )
34 {
35 // pid_t id = wait(NULL);
36 int status =0 ;
37 pid_t id= waitpid(-1 , &status ,0 );
38
39 if( id>0 )
40 {
41 printf("wait %d success,exit code:%d\n", id,WEXITSTATUS(status ) ) ; //id ,退出码
42 }
43
44 }
45
46 sleep(5);
108 return 0 ;
109 }
options:
1、options为0时,父进程阻塞等待
2、options为WNOHANG时,若等待的子进程没有结束,则waitpid函数直接返回0,不予以等待。若正常结束,则返回该子进程的pid
非阻塞轮询
options为WNOHANG时,父进程等待子进程中实现非阻塞
例如,父进程可以隔一段时间调用一次waitpid函数,若是等待的子进程尚未退出,则父进程可以先去做一些其他事,过一段时间再调用waitpid函数读取子进程的退出信息。
cpp
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 #define N 10
7 void Runchild ()
8 {
9 int cnt =5 ;
10 while(cnt)
11 {
12 printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
13 sleep(1);
14 cnt--;
15 }
16 }
17 int main()
18 {
48 pid_t id = fork() ;
49 if(id <0)
50 {
51 perror("fork");
52 return 1 ;
53 }
54 else if( id == 0 )
55 {
56 //child
57
58 int cnt =3 ;
59 while(cnt)
60 {
61 printf("I am a child ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
62 cnt -- ;
63 sleep(1);
64 }
65 exit(11);
66 }
67 else // parent
68 {
78 int status =0 ;//输出型参数
79 //操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
80 while(1) //轮询
81 {
82
83 pid_t ret = waitpid( id, &status , WNOHANG ); //非阻塞等待
84
85 //等待成功
86 if( ret >0 ) //id是子进程的pid
87 {
88 //status存放的是子进程的退出信息
89 //7F 0111 1111
90 //0xFF
91 // printf("wait success,ret:%d, exit sig : %d, exit code:%d\n", ret, status&0x7F , (status>>8) &0xFF ); //打印的分别是 id, 信号, 退出码
92
93 if(WEXITSTATUS(status) )
94 {
95 printf("进程是正常跑完的,退出码:%d\n",WEXITSTATUS(status));
96 }
97 else
98 {
99 printf("进程出异常了\n");
100 }
101 break;
102 }
103 else if ( ret< 0 )
104 {
105 printf("wait failed\n");
106 break;
107 }
108 else //ret==0
109 {
110 printf("子进程还没有退出我再等等\n") ;
111 sleep(1);
112 }
113 }
114 sleep(3);
115
116
117 }
118 return 0 ;
119 }
如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注你们的每一次支持都将转化为我前进的动力!!!