个人主页:Lei宝啊
愿所有美好如期而遇
目录
进程等待的必要性
进程等待的方法
获取子进程status
进程等待的必要性
首先,子进程退出,如果父进程不去回收子进程资源,读取子进程的PCB,那么就会使子进程变成僵尸进程,进而导致内存泄漏,
另外,僵尸进程无法通过控制信号杀死,也就是说kill -9无法杀掉僵尸进程,因为僵尸进程本来就已经死了,只剩下PCB,在等待被读取。
进程通过等待的方式,回收子进程资源,读取进程PCB来获取子进程退出信息。(一般来说,如果父进程不做等待,那么父进程可能会比子进程先结束,那么子进程就会成为孤儿进程,这当然不是我们想看到的,看示例)。
我们可以看到父进程先退出,然后子进程成为孤儿进程,被操作系统所接管,几秒后子进程的资源被回收。
进程等待的方法
获取子进程status
接下来在这里我们来解释waitpid的参数
参数pid : -1 等待任意一个子进程,与wait等效。
>0 等待指定pid子进程。
参数status:32位,我们只看后16位,格式为8位退出码+7位信号编号,还有一位core dump标志
参数options:阻塞等待(参数为0)和非阻塞等待(参数为WNOHANG)
status我们用来获取进程的退出信息,我们先解释wait函数,看代码示例:
我们也就看到wait成功回收了子进程。
wait(&status)等同于waitpid(-1,&status,0),属于阻塞等待,也就是说,父进程会等待子进程结束再继续执行。
我们将下面的waitpid(-1,&status,0)替换为wait(&status),得到的现象是相同的。
在fork之后,父进程应该执行他的输出语句,应该和子进程是同步的,但是现象就是子进程先执行完,父进程等待子进程结束再继续执行。
status为什么是256呢?首先我们的进程是正常结束的,也就是没有异常信号,所以status后7位为0,而我们给子进程设置的退出码为1,则他的补码是这样的0000 0001,status 补码就是0000 0000 0000 0000 0000 0001 0000 0000,所以结果就是256。
接下来我们使用非阻塞等待:
我们这里使用基于非阻塞的轮询访问,也就是循环去调waitpid函数,同时也可以对比得出wait会阻塞在该行直到子进程结束,而waitpid不会阻塞,子进程未结束返回给rid为0,继续向下执行,如果rid>0,也就是子进程结束,返回的值为子进程的pid,rid<0,则异常,我们直接退出。
补充
- wait和waitpid是系统调用,这个我们是清楚的,而且他们是为了获取退出的子进程的退出码和信号,我们就有疑问,难道父进程不可以直接获取,或者说我们直接定义一个变量。我们自己获取吗?是的,不可以,首先进程之间是相互独立的,父进程无法直接去读取子进程的数据,因为进程是操作系统所管理的,他的数据不允许用户直接读取,只能通过系统调用,也就是我们的wait和waitpid获取,然后通过我们上面提到的方式组合在status里,返回给父进程。
- 同时,我们可以从status中通过位运算提取出信号和退出码: 提取信号:status & 0x7F 也就相当于&0000 0000 0000 0000 0000 0000 0111 1111 提取退出码:stauts >> 8 & 0xFF, 也就相当于& 0000 0000 0000 0000 0000 0000 1111 1111
- waitpid,我们基于非阻塞轮询访问时,父进程可以进行其他操作,执行其他函数。