模拟bash管道时wc子进程挂起问题排查求助
管道挂起问题排查与修复
问题根源
你的代码导致wc进程挂起的核心原因是管道的引用计数未正确减少,使得下游进程一直等待输入EOF,具体有两点:
- 子进程未关闭冗余的管道描述符:dup2将管道复制到标准输入/输出后,原管道描述符仍处于打开状态,增加了管道的引用计数;
- 父进程关闭管道的时机滞后:fork完所有子进程后才关闭管道,导致上游管道的写端被父进程长时间持有,下游进程(如
grep)无法及时收到EOF,进而阻塞wc。
修复后的代码
#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/wait.h> #define CMD_NUM 3 int main(void) { pid_t pids[CMD_NUM]; int pipeFds[CMD_NUM - 1][2]; // 创建所有管道 for (int i = 0; i < CMD_NUM - 1; i++) { if (pipe(pipeFds[i]) == -1) { perror("Error creating pipes"); exit(EXIT_FAILURE); } } // 第一个子进程:ls pids[0] = fork(); if (pids[0] == 0) { // 关闭不需要的读端 close(pipeFds[0][0]); // 重定向stdout到管道写端 dup2(pipeFds[0][1], STDOUT_FILENO); // 关闭冗余的管道写端描述符 close(pipeFds[0][1]); char* argv[] = {"ls", "-l", NULL}; execvp("ls", argv); perror("Error ls execvp"); exit(EXIT_FAILURE); } // 父进程关闭该管道的写端(已交给ls子进程) close(pipeFds[0][1]); // 中间子进程:grep for (int i = 1; i < CMD_NUM - 1; i++) { pids[i] = fork(); if (pids[i] == 0) { // 处理输入管道(前一个管道) close(pipeFds[i-1][1]); dup2(pipeFds[i-1][0], STDIN_FILENO); close(pipeFds[i-1][0]); // 处理输出管道(当前管道) close(pipeFds[i][0]); dup2(pipeFds[i][1], STDOUT_FILENO); close(pipeFds[i][1]); char* argv[] = {"grep", "ma", NULL}; execvp("grep", argv); perror("Error in execvp"); exit(EXIT_FAILURE); } // 父进程关闭该子进程使用的管道端 close(pipeFds[i-1][0]); close(pipeFds[i][1]); } // 最后一个子进程:wc pids[CMD_NUM - 1] = fork(); if (pids[CMD_NUM - 1] == 0) { int last_pipe_idx = CMD_NUM - 2; // 关闭不需要的写端 close(pipeFds[last_pipe_idx][1]); // 重定向stdin到管道读端 dup2(pipeFds[last_pipe_idx][0], STDIN_FILENO); // 关闭冗余的管道读端描述符 close(pipeFds[last_pipe_idx][0]); char* argv[] = {"wc", NULL}; execvp(argv[0], argv); perror("Error en execvp"); exit(EXIT_FAILURE); } // 父进程关闭该管道的读端(已交给wc子进程) close(pipeFds[CMD_NUM - 2][0]); // 等待最后一个子进程结束 waitpid(pids[CMD_NUM - 1], NULL, 0); return 0; }
关键修复点说明
- 子进程关闭冗余描述符:每次dup2完成后,立即关闭原管道描述符,确保管道的引用计数仅保留必要的读写端;
- 父进程及时关闭管道端:fork完每个子进程后,父进程立即关闭该子进程已经接管的管道端,避免持有不必要的描述符导致下游进程阻塞;
- 补充必要头文件:添加了
<sys/wait.h>头文件,消除waitpid隐式声明的警告。
内容的提问来源于stack exchange,提问作者lzy917
相关产品推荐
相关产品推荐

