为何管道读端关闭时未触发SIGPIPE信号?
你遇到的问题核心在于父进程没有关闭管道的文件描述符,导致管道的读端并未完全关闭,因此不满足SIGPIPE的触发条件。
关键原理回顾
根据管道的行为规则:只有当管道的所有读端文件描述符都被关闭时,进程向管道写入才会触发SIGPIPE信号(默认会终止进程),同时write调用会返回EPIPE错误。
你的代码中的问题
在你的main函数中,创建完两个子进程后,父进程仍然持有两对管道的全部4个文件描述符:
ask_to_calc[0](管道读端)、ask_to_calc[1](管道写端)calc_to_ask[0](管道读端)、calc_to_ask[1](管道写端)
当计算进程(calc_pid)退出时,它持有的ask_to_calc[0]会被内核回收,但父进程还拿着ask_to_calc[0]这个读端文件描述符!这意味着ask_to_calc管道的读端并没有完全关闭——系统中还有父进程持有它的读端。
这时候出题进程(ask_pid)向ask_to_calc管道写入数据时,因为管道还有读端存在(只是没有进程去读取数据),写入操作会进入阻塞状态(默认管道是阻塞模式),而不是触发SIGPIPE。
同时父进程在wait循环中等待子进程退出,出题进程阻塞在写入操作上无法退出,导致程序陷入阻塞,只输出calc terminated normally后就停住了。
解决方法:父进程关闭无用的管道文件描述符
父进程不需要使用这两对管道,所以在创建完两个子进程后,应该立即关闭所有管道的文件描述符,这样就能保证当子进程退出时,管道的对应端会被完全关闭。
修改main函数,在创建完两个子进程后添加以下代码:
// 父进程关闭所有管道文件描述符,不再持有管道的读写端 close(ask_to_calc[0]); close(ask_to_calc[1]); close(calc_to_ask[0]); close(calc_to_ask[1]);
验证效果
修改后运行程序:
- 计算进程(
calc_pid)会立即退出(因为你清空了calc_a_plus_b的代码) - 此时
ask_to_calc管道的所有读端都已关闭(父进程和计算进程都关闭了对应的fd) - 出题进程(
ask_pid)尝试向管道写入时,会触发SIGPIPE信号被终止 - 父进程会输出:
calc terminated normally ask terminated due to signal 13
总结
管道的SIGPIPE触发条件依赖于全局范围内所有读端fd是否都关闭,父进程持有的管道fd很容易被忽略,这是管道编程中常见的坑。确保不需要使用管道的进程(比如这里的父进程)及时关闭管道fd,就能避免这类问题。
内容的提问来源于stack exchange,提问作者TsReaper

