Linux进程等待

1. 进程等待必要性

1. 子进程退出,父进程如果不管不顾,就可能造成 僵尸进程 的问题,进而造成内存泄漏。
2. 另外,进程一旦变成僵尸状态,那就刀枪不入,“ 杀人不眨眼 kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
3. 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。

2. 进程等待的方法

进程等待用于回收子进程的资源,避免子进程的PCB一直占用资源造成内存泄漏,并且可以获取子进程的退出信息,得知子进程任务的执行情况,进程等待主要通过两个系统调用接口 wait waitpid 来完成。

wait接口

使用wait接口,需要包含头文件<sys/types.h><sys/wait.h>,其接收一个int*指针,该参数是一个输出型参数,用于返回子进程的相关推出信息,不关心则可以设置成为NULL。返回值是一个int类型:返回值大于0:返回等待到的子进程的pid;返回值小于0:等待失败 

例如,我们有这样一个test1.c文件,先通过fork创建了一个子进程,子进程进入if语句,进行三秒倒计时,然后退出,并且退出码为3。父进程则进行六秒倒计时,通过wait函数进行等待,传入指针&status,接收返回值ret,最后输出statusret的值。

我们可以输入 while true;do ps -ajx | head -1;ps -ajx | grep test1 | grep -v grep;echo "---------------------------------------------------------------";sleep 1;done;   来实时检测父进程和子进程的状态

首先,子进程的pid为853483,而wait的返回值就是子进程的pid。其次,status一开始被初始化为0,wait之后,status = 768,可知wait确实会修改传入的参数。

status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。一旦子进程退出,操作系统就会将退出信息拷贝到status中,然后就可以将其带出来。如果传递NULL,表示不关心子进程的退出状态信息。 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。 status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status16比特位):

灰色部分:status是一个int类型,占32比特,但是前16比特是无效的,不填入任何内容
黄色部分:第8 - 15位,共8比特,用于表示wait到的子进程的退出码
绿色部分:第7位,core dump标志位本博客不关心该位置
蓝色部分:第0 - 6位,共7比特,用户表示wait到的子进程的退出信号

那么我们要从status中提取出退出码退出信号,就可以要对其进行位操作:

1. status直接与01111111进行按位与&,就能得到退出信号01111111的十六进制表示为0X7F

int signal = status & 0x7f;

2. status右移8位后,与11111111进行按位与&,就能得到退出码11111111的十六进制表示为0XFF

int code = (status >> 8) & 0xff;

接着我们对test1.c文件进行修改,增加如下代码并再次运行。

Linux还给用户提供了两个宏函数,用于检测status:

WIFEXITED:检测进程是否正常退出,返回一个布尔值,如果进从正常退出,返回真
WEXITSTATUS:提取子进程的退出码,也就是第8 - 15位if(WIFSIGNALED(status))

waitpid接口

进程等待的另外一个接口是waitpid接口,需要包含头文件<sys/types.h><sys/wait.h>,一个进程是可以有多个子进程的,一个wait只能等待一个子进程,如果有多个子进程,那么wait函数等待第一个结束的子进程。而waitpid则是针对pid来对进程进行等待,当然也可以在第一个参数取 -1 就可以任意等待一个子进程。

参数:
1. pid
pid=-1, 等待任一个子进程。与 wait 等效。
pid>0, 等待其进程 ID pid 相等的子进程。
2. status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): WIFEXITED 非零,提取子进程退出码。(查看进程的退出码)
3. options:
WNOHANG: pid 指定的子进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。若正常结束,则返回该子进程的ID
返回值:
1. 当正常返回的时候 waitpid 返回收集到的子进程的进程 ID
2. 如果设置了选项 WNOHANG, 而调用中 waitpid 发现没有已退出的子进程可收集 , 则返回 0
3. 如果调用中出错 , 则返回 -1, 这时 errno 会被设置成相应的值以指示错误所在;

现在我们来尝试多进程的进程等待,我们有这样一个test2.c的文件,创建了五个子进程并进行五次进程等待。

我们再来看看第三个参数。第三个参数用于控制进程等待的模式:

  • 0:进行阻塞等待
  • WNOHANG:进行非阻塞等待

如果退出多个子进程需要使用多个wait。如果子进程不退出,父进程默认在wait的时候调用系统调用的时候,也就不返回,默认叫做阻塞状态。

父进程在wait的时候,什么也不做,进入阻塞状态,直到wait成功。而非阻塞等待不一样,进行非阻塞等待时,如果本次waitpid没有等待到,那么父进程不会阻塞,waitpid直接返回0,表示本次等待没有等待到子进程。此时父进程就可以空出时间去完成别的任务,而不是傻乎乎地死等了。

例如,我们有一个test3.c文件如下

以上代码中先通过fork创建了一个子进程,子进程sleep五秒。父进程陷入一个while死循环,每次循环开始,都waitpid一次,以WNOHANG模式。由于该模式不会阻塞,只要当前子进程没有结束,那么waitpid直接返回,去执行后面的if语句。

如果当前返回值为0,说明当前子进程没有结束,那么父进程可以去做些别的事情,一秒后再回来检测子进程有没有结束。如果当前返回值> 0,说明子进程结束了,waitpid也成功了,此时返回值就是子进程的pid ,跳出循环。


 

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我要满血复活

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值