MIT OCW 6.828课程Shell作业:Pipe功能无输出问题求助
管道命令无输出问题排查
我正在完成MIT 6.828操作系统工程课程的首个作业——实现一个简易Shell。单独执行echo、grep等命令均正常,但执行管道命令echo "hahaha" | grep "h"时没有任何输出。已确认pipe左右命令的argv参数正常,以下是我的pipe实现代码片段,求帮忙排查:
case '|': pcmd = (struct pipecmd*)cmd; struct execcmd *left = (struct execcmd*)pcmd->left; struct execcmd *right = (struct execcmd*)pcmd->right; int i; int pd[2]; pipe(pd); int pid1 = fork1(); int pid2 = fork1(); if(!pid1) { // close(STDOUT_FILENO); // close(pd[0]); execvp(left->argv[0], left->argv); dup2(pd[1], 1); exit(0); } if(!pid2) { // close(STDIN_FILENO); // close(pd[1]); dup2(pd[0], 0); execvp(right->argv[0], right->argv); exit(0); } if(pid1 > 0) { int status2; int status; if(waitpid(pid1, &status, 0) == -1){ perror("waitpid1"); } if(waitpid(pid2, &status2, 0) == -1) { perror("waitpid2"); }; // close(pd[0]); // close(pd[1]); } break;
注:仅实现了单管道功能,知晓多管道需循环处理。
问题分析与修复
你的代码存在几个关键问题,导致管道无法正常工作:
execvp执行顺序错误:在子进程中,你先调用了execvp,再做dup2重定向。但execvp成功后会直接替换当前进程的代码段,后续的dup2根本不会执行,导致左边命令的输出没有重定向到管道。必须先完成重定向,再调用execvp。未关闭管道的未使用端:管道的两端需要在不需要的进程中关闭,否则会导致读端进程(比如grep)一直等待,因为系统认为还有进程持有管道的写端,不会返回EOF。
父进程未及时关闭管道:父进程在wait之前应该关闭管道的两端,避免子进程因为管道引用未释放而卡住。
修复后的代码如下:
case '|': pcmd = (struct pipecmd*)cmd; struct execcmd *left = (struct execcmd*)pcmd->left; struct execcmd *right = (struct execcmd*)pcmd->right; int pd[2]; pipe(pd); int pid1 = fork1(); if(!pid1) { // 左命令:将标准输出重定向到管道写端 dup2(pd[1], STDOUT_FILENO); // 关闭未使用的管道读端 close(pd[0]); // 关闭管道写端(dup2后原描述符已无用) close(pd[1]); execvp(left->argv[0], left->argv); // execvp失败才会走到这里 perror("execvp left"); exit(1); } int pid2 = fork1(); if(!pid2) { // 右命令:将标准输入重定向到管道读端 dup2(pd[0], STDIN_FILENO); // 关闭未使用的管道写端 close(pd[1]); // 关闭管道读端(dup2后原描述符已无用) close(pd[0]); execvp(right->argv[0], right->argv); // execvp失败才会走到这里 perror("execvp right"); exit(1); } // 父进程关闭管道两端,避免子进程等待 close(pd[0]); close(pd[1]); // 等待子进程结束 int status; if(waitpid(pid1, &status, 0) == -1){ perror("waitpid1"); } if(waitpid(pid2, &status, 0) == -1) { perror("waitpid2"); } break;
关键修改点说明:
- 调整
dup2和execvp的顺序,确保重定向先完成,再执行命令。 - 在每个子进程中关闭管道的未使用端,以及dup2后不再需要的原管道描述符。
- 父进程在fork完两个子进程后立即关闭管道两端,再等待子进程退出。
内容的提问来源于stack exchange,提问作者a6i09per5f
相关产品推荐
相关产品推荐

