使用管道通信时子进程数超过处理器数量是否会导致进程阻塞?
问题解答
问题1:子进程数量大于处理器数量是否会导致阻塞
不会。操作系统进程调度采用时间片轮转机制,子进程数量超过CPU核心数只会带来执行调度延迟,不会出现永久阻塞。你遇到的子进程残留S状态是逻辑死锁导致的,和CPU核心数无关:
管道默认缓冲区大小有限(通常为4KB~64KB),你的代码逻辑是父进程先调用waitpid等待子进程退出,再读取管道内容。如果子进程写入管道的数据量超过缓冲区上限,write系统调用会进入阻塞状态,等待父进程读取管道释放缓冲区;但此时父进程卡在waitpid等待子进程退出,不会执行读操作,双方互相等待形成死锁,子进程就会一直停留在S(可中断睡眠)状态。
额外的代码缺陷也会加重该问题:
- 子进程未关闭不属于自己的管道文件描述符:
fork会继承父进程所有打开的fd,每个子进程除了自身使用的写端fd外,其他所有管道的fd都未关闭,会导致管道引用计数无法清零,就算子进程关闭自己的写端,父进程读管道时也收不到EOF,触发读阻塞。 - 父进程仅在子进程正常退出时才关闭管道fd,若子进程异常退出会出现fd泄漏,进一步引发资源占满导致的阻塞。
问题2:read使用的fd从4开始、write使用的fd从5开始的成因
Linux系统中每个进程启动时默认打开3个标准文件描述符:
- 0:标准输入
stdin - 1:标准输出
stdout - 2:标准错误
stderr
内核分配文件描述符遵循最小可用编号原则,调用pipe()创建管道时,会连续分配两个未被占用的最小fd,第一个为管道读端,第二个为写端。
你观察到读端从4、写端从5开始,说明程序在执行当前function函数之前,已经打开了1个其他文件/套接字,占用了fd=3的位置,因此第一个管道的读端分配到4、写端分配到5,后续每个管道的fd依次加2,就会出现读端为偶数、写端为奇数,从4、5开始的现象,属于正常情况。
修复建议
- 子进程fork成功后,先遍历关闭所有不属于自己的管道fd,仅保留自身需要使用的写端fd
- 调整父进程逻辑:先读取所有管道的内容,再执行
waitpid回收子进程,或使用IO多路复用(select/epoll)同时监听所有管道读端,边读边处理退出的子进程 - 无论子进程退出状态是否正常,都关闭对应的管道fd,避免资源泄漏
内容的提问来源于stack exchange,提问作者heisthere
相关产品推荐
相关产品推荐

