管道实现异常排查:为何ls |grep "main-pipe"|wc陷入无限循环?
问题排查与修复
核心问题:父进程没及时关闭管道的闲置端
你的代码里,父进程创建管道后,没及时关掉不用的管道描述符,导致后续进程没法检测到管道的EOF,直接卡成无限循环:
ls子进程结束后,父进程还攥着pipe1的**写端(pipe1[1])**没关。这时候grep从pipe1读数据,虽然ls已经关了自己的写端,但父进程的写端还开着,grep会觉得可能还有数据进来,就一直堵在读取操作上不肯退出。父进程的wait(0)就一直等grep结束,俩进程互相耗着,就无限循环了。- 同理,处理
wc之前,父进程也得把没用的管道描述符都关干净。
其他坑点
execl("wc", "wc")没用到execlp,也没写wc的完整路径(比如/usr/bin/wc),系统可能找不到这个命令,直接执行失败。改成execlp("wc", "wc", (char*)NULL)就行,和grep的调用方式保持一致。
修复后的代码
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> int main(int argc, char* argv[]) { if (argc > 1) { printf("Please don't provide additional arguments.\n"); exit(1); } int pipe1[2]; pipe(pipe1); int pid = fork(); if (pid == 0) { // 重定向标准输出到pipe1写端 close(1); dup(pipe1[1]); close(pipe1[1]); close(pipe1[0]); execlp("ls", "ls", (char*)NULL); // 补全NULL参数,符合调用规范 perror("execlp ls failed"); exit(1); } // 父进程关掉pipe1写端,反正不用写了 close(pipe1[1]); wait(0); int pipe2[2]; pipe(pipe2); pid = fork(); if (pid == 0) { close(0); dup(pipe1[0]); close(pipe1[0]); close(1); dup(pipe2[1]); close(pipe2[1]); close(pipe2[0]); execlp("grep", "grep", "main-pipe", (char *)NULL); perror("execlp grep failed"); exit(1); } // 父进程关掉pipe1读端和pipe2写端,闲置的都清掉 close(pipe1[0]); close(pipe2[1]); wait(0); close(0); dup(pipe2[0]); close(pipe2[0]); execlp("wc", "wc", (char*)NULL); perror("execlp wc failed"); exit(1); }
关键修复说明
ls启动后,父进程立刻关pipe1[1],这样ls结束关了自己的写端后,pipe1所有写端都关闭了,grep读到EOF就会正常退出,不会一直等。grep启动后,父进程关pipe1[0]和pipe2[1],避免这些闲置描述符搞事情。- 把
execl改成execlp并补(char*)NULL,保证命令能找到,加perror方便出问题时查原因。
内容的提问来源于stack exchange,提问作者Erik Nouroyan
相关产品推荐
相关产品推荐

