You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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; 

注:仅实现了单管道功能,知晓多管道需循环处理。


问题分析与修复

你的代码存在几个关键问题,导致管道无法正常工作:

  1. execvp执行顺序错误:在子进程中,你先调用了execvp,再做dup2重定向。但execvp成功后会直接替换当前进程的代码段,后续的dup2根本不会执行,导致左边命令的输出没有重定向到管道。必须先完成重定向,再调用execvp。

  2. 未关闭管道的未使用端:管道的两端需要在不需要的进程中关闭,否则会导致读端进程(比如grep)一直等待,因为系统认为还有进程持有管道的写端,不会返回EOF。

  3. 父进程未及时关闭管道:父进程在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 19:45:31