在模拟实现之前,我们首先要理解什么是阻塞状态?
操作系统将不满足某种条件的进程从运行队列中拿出,将其状态设置为其他状态并放入等待队列中,在此等待队列中的进程的都处于阻塞状态。
一、父进程为什么要等待?
1、僵尸进程,造成内存泄漏
2、父进程得管理子进程,所以父进程派给子进程的任务完成的如何,我们都需要知道,如,子进程运行完成,运行结果对还是不对,或者是否正常退出
3、父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
二、等待的方法
- wait方法
pid_t wait(int *status);
返回值:成功返回被等待进程的pid,失败返回-1
参数:输出型参数,获取子进程状态,不关心则可设置为NULL - 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非0,提取子进程退出码。(查看进程的退出码)
options:
0:阻塞式等待
WNOHANG(非阻塞式等待):若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待,若正常结束,则返回该子进程的ID。
注意: - 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息
- 如果在子进程存在且正常运行时调用,则进程可能阻塞
- 如果不存在该子进程,则立即出错返回
三、模拟实现
- 进程的阻塞等待方式
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <error.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fork error\n");
return 1;
}
else if(pid == 0)
{
//child
printf("child is run,pid is :%d\n",getpid());
sleep(5);
exit(210);
}
else
{
//parents
int status = 0;
//阻塞式等待任意一个子进程
pid_t ret = waitpid(-1, &status, 0);
printf("wait...\n");
if(WIFEXITED(status) && ret == pid)
{
//等成功,提取出退出码
printf("wait success, child return code is :%d.\n", WEXITSTATUS(status));
}
else
{
printf("wait failed...\n");
return 1;
}
}
return 0;
}
输出结果:
子进程未退出之前,父进程啥都不干,一直在等待,5秒后子进程退出,才提取出退出码。
- 进程的非阻塞等待方式
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <error.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fork error\n");
return 1;
}
else if(pid == 0)
{
//child
printf("child is run,pid is :%d\n",getpid());
sleep(5);
exit(1);
}
else
{
//parents
int status = 0;
pid_t ret = 0;
do
{
//非阻塞式等待,没有已退出的子进程,返回值是0
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 success, child return code is :%d.\n", WEXITSTATUS(status));
}
else
{
printf("wait failed...\n");
return 1;
}
}
return 0;
}
输出结果:
子进程没有退出,父进程每隔1秒去检测一次,若没有检测到已退出的子进程,就立即退出。