首先进程池看看代码怎么写的
https://gitee.com/ljh0617/linux_test/blob/master/11-17/3.pipe_use/ProcessPool.cc
我们对子进程分配到的管道读文件描述符进行了重定向,让他改为从0读,这和清理工作无关,只是这么设计让子进程不再有键盘输入的概念。
看看子进程的文件描述符分配,全部分配到的读 fd 都是3,这正常吗?
是正常的,父进程利用pipe来打开读写fd,因为父进程要写端,关闭读端,也就关闭了3
第一次 父进程 打开的是 3 4
后续 3 5
3 6 这样遵循了文件描述符分配方式,那么子进程得到的fd就都是3
那这好像和清理关系也不大。
为了方便,这里是直接忽略了0,1,2标准输入输出流
直接0 1 fork创建子进程,关闭自己的0 , 也就是读端,子进程关闭自己的写段1。
继续创建子进程建立管道 0 2,0依旧是子进程的读端 ,但是此时子进程的fd表中的1是继承了父进程的1写段,也就是让子进程中有写段指向了第一个子进程,此时第一个管道就有两个写段,继续创建第三个子进程又会继承父进程的fd表 ,就会导致第一个管道写段越来越多。
根据管道的四种情况,我们让写端关闭,读端就会读到0,此时break,子进程就会走到exit结束进程。
结束进程我们预期的是让waitpid来进程等待。
此时就会有不同的写法回收
我们先来看看第一种,关闭一个文件描述符,就等待一个进程,此时结果就是代码卡住了。
这样你先关闭第一个管道的读端fd,但是会有其他子进程文件描述符表中 从父进程继承过来的写端fd指向第一个管道,此时waitpid就会阻塞等待,因为有写端指向这个管道,读端不会收到0,也就不会exit,也就不会僵尸进程,也就不会被等待成功,所以被卡主了。
那我这样写两次循环,为什么可以呢?
此时重点放在最后一个管道和子进程上。
最后一个管道只有一个写端,他关闭了,这个进程能读到0结束符,进程也就是等待成功了,同时他指向上一个管道的写端也释放了,上个管道自己也因为for循环关闭了写端,则上一个管道的读端也能读到0,也就释放了,所以是这样倒着回收的。
目前这个bug差不多解决了。
那我就不想这样,我就想从根本上解决这个bug,怎么办?
利用数组保存父进程的写端,第一次的时候数组是空的,什么也没做
第二次进入子进程的时候,关闭了继承下来的写端,而且关闭的是子进程的,不是关闭父进程的写端,关闭父进程的写端就出错了。
第二次会关闭 继承到的第一个管道的写端,这样你用一个for循环,关闭一个fd,等待一个子进程就完美了。