文章目录
- 1.进程等待
- 1.1进程等待必要性
- 1.1.1为什么有进程等待这个概念
- 1.1.2进程等待是什么?
- 1.1.3进程等待具体干什么?
- 1.2进程退出方法:
- 2.具体代码实现
1.进程等待
1.1进程等待必要性
1.1.1为什么有进程等待这个概念
之前讲过,子进程退出,如果父进程不管不问,那么就可能会造成僵尸进程
的问题,僵尸进程杀不死,本节所讲的进程等待就是杀掉僵尸进程的方法,从而解决因为僵尸进程而导致的内存泄漏问题
。我们要通过进程等待,来获取子进程的退出情况,即:知道父进程我布置给子进程的任务完成的怎么样了,要么关心,也可能不关心(通过设置选项可以选择关心与否)。
1.1.2进程等待是什么?
通过系统调用wait/waitpid,来进行对进程运行状态进行检测与回收功能。
1.1.3进程等待具体干什么?
通过代码实现:父进程通过调用wait/waitpid进行僵尸进程的回收问题!
1.2进程退出方法:
wait方法:
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
例如如下代码:
#include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/wait.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 int main()
7 {
8 pid_t id = fork();
9 if(id == 0)
10 {
11 printf("I am a Child Process,pid:%d,ppid:%d\n",getpid(),getppid());
12 exit(1);
13 }
14 else if(id < 0)
15 {
16 perror("fork");
17 return 1;
18 }
19 else
20 {
21 while(1)
22 {
23 pid_t ret = wait(NULL);
24 if(ret < 0)
25 {
26 printf("wait failed\n");
27 break;
28 }
29 else if(ret >0)
30 {
31 printf("进程是正常跑完的, 退出码:%d\n",ret);
32 break;
33 }
34 else{
35 printf("进程还结束,请等待哦\n");
36 break;
37 }
38 }
39 }
运行结果为:
waitpid方法:
pid_ t waitpid(pid_t pid, int *status, int options)
;
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID
;
如果设置了选项WNOHANG
,而调用中waitpid发现没有已退出的子进程可收集,则返回0
;
如果调用中出错,则返回-1
,这时errno会被设置成相应的值以指示错误所在;
参数:
- pid:
Pid=-1,等待任一个子进程。与wait等效。Pid>0.等待其进程ID与pid相等的子进程
。 - status:WIFEXITED(status):
若为正常终止子进程返回的状态
,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码
。(查看进程的退出码) - options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
1.如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,
并且释放资源,获得子进程退出信息。
2.如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
3.如果不存在该子进程,则立即出错返回。
代码如下:
#include<stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6
7 #define N 10
8 void RunChild()
9 {
10 int cnt = 5;
11 while(cnt)
12 {
13 printf("I am Child Process, pid: %d, ppid:%d\n", getpid(), getppid());
14 sleep(1);
15 cnt--;
16 }
17 }
18 int main()
19 {
20 /*for(int i=0;i<N;i++)
21 {
22 pid_t id = fork();
23 if(id == 0)
24 {
25 RunChild();
26 exit(i);
27 }
28 printf("Creat Child Process: %d Success\n",id);
29 }
30 return 0;*/
31 pid_t id = fork();
32 if(id < 0)
33 {
34 perror("fork");
35 return 1;
36 }
37 else if (id ==0)
38 {
39 int cnt =5;
40 while(cnt)
41 {
42 printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
43 cnt--;
44 sleep(1);
45 }
46 exit(11);
47 }
48 else{
49 /* int cnt = 5;
50 while(cnt)
51 {
52 printf("I am father, pid:%d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
53 cnt--;
54 sleep(1);
55 }*/
56 int status = 0;
57 while(1)
58 {
59 pid_t ret = waitpid(id, &status, WNOHANG); //非阻塞
60 if(ret > 0)
61 {
62 if(WIFEXITED(status))
63 {
64 printf("进程是正常跑完的,退出码为:%d\n",WEXITSTATUS(status));
65 }
66 else
67 {
68 printf("进程异常退出了。\n");
69 }
}
70 break;
71 }
72 else if(ret <0)
73 {
74 printf("wait failed\n");
75 break;
76 }
77 else{
78 printf("你好了没?子进程还没有退出,我在等等...\n");
79 sleep(1);
80
81 }
82
83 }
84 }
85 return 0;
86 }
获取子进程status:
- wait和waitpid,
都有一个status参数
,该参数是一个输出型参数
,由操作系统填充
。 - 如果传递NULL,表示不关心子进程的退出状态信息。否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
- status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):
测试代码如下:
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main( void )
{
pid_t pid;
if ( (pid=fork()) == -1 )
perror("fork"),exit(1);
if ( pid == 0 )
{
sleep(20);
exit(10);
}
else
{
int st;
int ret = wait(&st);
if ( ret > 0 && ( st & 0X7F ) == 0 )
{ // 正常退出
printf("child exit code:%d\n", (st>>8)&0XFF);
}
else if( ret > 0 )
{ // 异常退出
printf("sig code : %d\n", st&0X7F );
}
}
}
测试结果为:
2.具体代码实现
进程的阻塞等待方式:
int main()
{
pid_t pid;
pid = fork();
if(pid < 0)
{
printf("%s fork error\n",__FUNCTION__);
//__FUNCTION__: ,这是一个预编译器内置宏,它会被替换为当前函数的名称。
//所以,如果你在函数my_function中调用了这条语句,__FUNCTION__会被替换为"my_function"。如果fork函数出错(通常fork的返回值是-1表示出错),则打印出“函数名 fork error”并换行。
return 1;
}
else if( pid == 0 )
{ //child
printf("child is run, pid is : %d\n",getpid());
sleep(5);
exit(257);
}
else
{
int status = 0;
pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S
printf("this is test for wait\n");
if( WIFEXITED(status) && ret == pid )
{
printf("wait child 5s success, child return code is:%d.\n",WEXITSTATUS(status));
}
else
{
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}
进程的非阻塞等待方式:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0)
{
printf("%s fork error\n",__FUNCTION__);
return 1;
}
else if( pid == 0 )
{ //child
printf("child is run, pid is : %d\n",getpid());
sleep(5);
exit(1);
}
else
{
int status = 0;
pid_t ret = 0;
do
{
ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
if( ret == 0 )
{
printf("child is running\n");
}
sleep(1);
}while(ret == 0);
if( WIFEXITED(status) && ret == pid )
{
printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
}
else
{
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}