进程等待
之前说,子进程退出,父进程如果不管不顾,就可能造成“僵尸进程”的问题,进而造成内存泄露,进而,进程一旦变成僵尸状态,杀人不眨眼的”kill -9”也无能为力,因为谁也没有办法杀死一个已经死去的进程,最后,父进程派给子进程的任务完成的如何,我们需要知道,如子进程运行完成,结果是还是不对,或者是否正常退出,我们需要知道
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
进程等待方法
wait()
头文件:#include<sys/wait.h>
#include<sys/type.h>
原型
pid_t wait(int *status)
返回值:
成功:返回被等待进程(子进程)pid
失败:返回-1
参数:输出型参数,获取子进程退出状态,不关心则可以设置为NULL
waitpid()
头文件: #include<sys/type.h>
#include<sys/wait.h>
返回值:
(1)当正常返回的时候waitpid返回收集到的子进程的进程ID
(2)如果设置了选项WNOHANG,而调用中waitpid发现已经没
有已经可以退出的子进程可收集,则返回0
(3)如果调用中出错,则返回-1,这时errno会被设置成相应的
值以指示错误所在
原型
pid_t waitpid(pid_t pid,int *status,int options)
参数说明:
1.pid
pid=-1,等待任一个子进程,与wait等效
pid>0,等待其进程ID与pid相等的子进程
2.status
WIFEXITED(status): 若为正常终止子进程返回的状态,则为
真(此参数是查看进程是否是正常退出)
WEXITSTATUS(status): 若WEXITSTATUS非零,提取子进程退
出码(查看进程的退出码)
3.options
WNOHANG:若pid指定的子进程没有结束,则waitpid()函数
返回0,不予以等待,若正常结束,则返回该子进程的ID
(1)如果子进程已经退出,调用wait/waitpid会立即返回,并且释放资源,获得子进程退出信息
(2)如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞
(3)如果不存在该子进程,则立即出错返回
(4)子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止)
获取子进程status
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充
如果传递NULL,表示不关心子进程的退出状态信息,
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位)
正常终止
status & 0x7f, 如果是0的话,那就表示正常退出
0xff是0111111,status 按位与0x7f,表示取出status的低7位
当正常终止的时候,status高8位就是子进程的退出码
status & 0xff,就是取出status的高8位
异常终止
status & 0x7f, 不为0的话,那就表示异常退出
判断子进程正常/异常退出的时候:
status & 0x7f(status按位与0x7f)的时候,按位与7位就好,如果按位与的结果是0,那就表示子进程正常退出,按位与的结果不是0,那就表明子进程异常退出
阻塞式等待&非阻塞式等待
1.阻塞式等待
子进程先执行,而且父进程会等待子进程执行结束,再去执行自己
2.非阻塞式等待
子进程和父进程谁先执行不确定,父进程如果先执行的话,父进程会一边等待
子进程而另一边又做自己的事情,即就是等待子进程和执行自己的事情是同步
进行的
示例
进程的阻塞等待方式
wait()
代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
if((pid=fork())==-1)
{
perror("fork()");
exit(1);
}
if(pid==0)//child
{
sleep(30);
exit(10);
}
if(pid>0)
{
int status;
int ret=wait(&status);
if(ret>0&&((status&0x7f)==0))
{
printf("child exit code:%d\n",(status>>8)&0xff);
}
//status>>8表示把status的低8位右移出去
//(status>>8)&0xff表示把status的低8位右移出去(系统会给status会补够32位),然后与0xff按位与,得到status的低8位,此结果就是子进程的退出码
else if(ret>0)
{
//异常退出
printf("sig code: %d\n",status&0x7f);
}
}
return 0;
}
运行结果
等待30s之后
此运行结果是子进程正常退出的场景
在另一个终端把子进程kill掉
ps: ps -ef | grep a.out 是查看进程的pid
kill -9+pid 是杀死进程ID为pid的进程
例图中 kill -9 2612 就是杀死进程为2612的进程
进程退出信号是:9号信号
waitpid()
代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>//waitpid()的头文件
int main()
{
pid_t pid=fork();
if(pid<0)
{
perror("fork");
exit(1);
}
if(pid==0)
{
printf("child is run,child pid is :%d\n",getpid());//getpid()得到的是当前进程的pid
sleep(5);
exit(10);
}
if(pid>0)
{
int status=0;
pid_t ret=waitpid(-1,&status,0);//阻塞式等待,等待5秒
printf("this is for test wait.\n");
if(WIFEXITED(status)&&(ret==pid))
{
printf("child is 5s succes.child exit code is :%d\n",WEXITSTATUS(status));
}
else if(ret>0)
{
printf("wait child failed.\n");
exit(1);
}
}
return 0;
}
运行结果:
等待5秒过后
阻塞式等待,父进程等待子进程执行,然后在执行自己
进程的非阻塞等待方式
waitpid()
代码:
include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
pid_t pid=fork();
if(pid<0)
{
perror("fork");
exit(1);
}
if(pid==0)
{
printf("child is run,child pid is :%d\n",getpid());
sleep(5);
printf("child is run.\n");
exit(10);
}
if(pid>0)
{
int status=0;
pid_t ret=0;//记得需要声明ret
do
{
ret=waitpid(-1,&status,WNOHANG);//非阻塞式等待
if(ret==0)
{
printf("child is running,\n");
}
sleep(1);
}while(ret == 0);//ret!=0表示子进程结束
if(WIFEXITED(status)&&(ret==pid))
{
printf("wait child 5s success,child exit code is:%d\n",WEXITSTATUS(status));
}
else
{
printf("wait child failed.\n");
exit(1);
}
}
return 0;
}
运行结果:
非阻塞式等待,就是谁先执行不一定,在此代码中,是父进程先执行的,然后让父进程睡眠1s,从而让子进程执行,子进程执行到sleep(5)时,此时又去执行父进程,但是子进程还没有执行结束,故一直执行while循环,5s过后,又再次遇到sleep(1),又去执行子进程,这时子进程就会结束,父进程继续完成自己的工作
若WIFEXITED(status)为真---->进程正常退出
若WEXITSTATUS(status)>0---->WEXITSTATUS(status)表示子进程的退出码