在C语言中实现管道时Shell挂起,无法执行ps -j | more命令
我来帮你分析下代码里的问题,以及怎么修复这个挂起的情况:
核心问题分析
你的代码出现挂起主要有两个关键原因:
1. 父进程未及时关闭管道的两端
当创建管道后,父进程、两个子进程都会持有管道读写两端的文件描述符。ps子进程只需要写管道,more子进程只需要读管道,但父进程必须关闭管道的所有两端——如果父进程不关闭,more会一直认为还有进程可能往管道里写数据,就会持续阻塞等待输入,永远不会退出,导致程序挂起。
2. 等待子进程的次数不足
你只循环等待了1次子进程退出,但实际fork了两个子进程,这会导致其中一个子进程变成僵尸进程,同时父进程可能提前退出,进一步引发终端状态混乱,间接加剧挂起问题。
修复后的完整代码
#define BUF_SIZE 100 #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> int main() { pid_t pid1, pid2; int fd[2]; char *argv1[] = { "/bin/ps", "-j", NULL }; char *argv2[] = { "/bin/more", NULL }; char prompt[] = "Press enter: "; char buffer[BUF_SIZE]; // 保留你原有的用户输入逻辑 write(STDOUT_FILENO, prompt, sizeof(prompt) - 1); ssize_t readIn = read(STDIN_FILENO, buffer, BUF_SIZE); if (readIn > 0) { buffer[readIn - 1] = '\0'; } printf("readIn: %zd\n", readIn); // 创建管道并检查错误 if (pipe(fd) == -1) { perror("pipe failed"); exit(EXIT_FAILURE); } // 第一个子进程:执行ps -j pid1 = fork(); if (pid1 == 0) { // 重定向stdout到管道写端 dup2(fd[1], STDOUT_FILENO); // 关闭所有未使用的管道描述符 close(fd[0]); close(fd[1]); // 执行ps命令,失败则打印错误 execvp(argv1[0], argv1); perror("execvp ps failed"); exit(EXIT_FAILURE); } else if (pid1 == -1) { perror("fork ps failed"); exit(EXIT_FAILURE); } // 第二个子进程:执行more pid2 = fork(); if (pid2 == 0) { // 重定向stdin到管道读端 dup2(fd[0], STDIN_FILENO); // 关闭所有未使用的管道描述符 close(fd[0]); close(fd[1]); // 执行more命令,失败则打印错误 execvp(argv2[0], argv2); perror("execvp more failed"); exit(EXIT_FAILURE); } else if (pid2 == -1) { perror("fork more failed"); exit(EXIT_FAILURE); } // 父进程必须关闭管道两端!这是解决挂起的关键 close(fd[0]); close(fd[1]); // 等待两个子进程全部结束,避免僵尸进程 int status; waitpid(pid1, &status, 0); waitpid(pid2, &status, 0); printf("finished\n"); exit(EXIT_SUCCESS); }
关键改动说明
- 父进程关闭管道两端:确保
more在ps结束后能收到EOF信号,知道输入已结束,不会无限阻塞。 - 完整等待子进程:用
waitpid分别等待两个子进程,或者也可以用while(wait(NULL) != -1)循环等待所有子进程退出,彻底避免僵尸进程问题。 - 增加错误处理:对
pipe、fork、execvp的返回值做检查,方便调试定位问题。 - 子进程清理管道描述符:子进程在重定向后,关闭所有未使用的管道描述符,避免不必要的资源泄漏。
这样修改后,程序就能正确实现ps -j | more的管道功能,不会再出现挂起的情况了。
内容的提问来源于stack exchange,提问作者doctopus
相关产品推荐
相关产品推荐

