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

自定义shell实现pipe与execvp时出现Bad file descriptors错误求助

自定义Shell管道实现错误修复方案

核心错误原因

  • 重定向逻辑完全错误:dup() 会返回新分配的文件描述符编号,你调用dup(p[1])后立刻关闭了这个新fd,等于管道端没有被保留到标准输入/输出的位置,程序访问fd 0/1时指向已关闭的文件,所以报坏文件描述符错误。
  • 进程调度逻辑错误:等待子进程1完全执行结束再fork子进程2,管道没有读端存在时,子进程1写入管道会触发SIGPIPE信号直接报错,且管道缓冲区有限,大输出场景下会直接死锁。
  • 使用fclose()操作标准流不合理:底层文件描述符重定向应该直接用系统调用close()操作fd 0/1,避免stdio层缓冲的额外影响。

修复步骤

  1. 子进程1(执行ls -l)重定向逻辑修正
    • 先关闭子进程不需要的管道读端p[0]
    • 直接关闭标准输出对应的文件描述符1
    • 调用dup2(p[1], 1),强制把管道写端复制到fd 1的位置,完成标准输出到管道的映射
    • 关闭原管道写端p[1],避免多余的文件描述符引用
  2. 子进程2(执行tail -n 2)重定向逻辑修正
    • 先关闭子进程不需要的管道写端p[1]
    • 直接关闭标准输入对应的文件描述符0
    • 调用dup2(p[0], 0),强制把管道读端复制到fd 0的位置,完成标准输入到管道的映射
    • 关闭原管道读端p[0],避免多余的文件描述符引用
  3. 父进程逻辑修正
    • 连续fork两个子进程,不要等待第一个子进程结束再fork第二个
    • 父进程立刻关闭自己持有的管道两端p[0]和p[1],避免管道无法正常结束
    • 最后等待两个子进程执行完成即可

修正后完整代码

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>

int main (int argc, char * argv[]){
    int debugMode=0;
    int p[2];
    int status;
    if (argc > 1 && strcmp(argv[1],"-d")==0)
        debugMode=1;
    if (pipe(p)<0) {
        perror("pipe create failed");
        return 1;
    }
    // 创建第一个子进程
    int child1= fork();
    if (child1 == 0)
    {
        if (debugMode == 1)
            fprintf(stderr, "Child 1 is redirecting stdout to write end of pipe.\n");
        // 关闭不需要的读端
        close(p[0]);
        // 关闭标准输出
        close(1);
        // 把管道写端复制到fd 1
        dup2(p[1], 1);
        // 关闭原管道写端
        close(p[1]);
        char *args[] = {"ls","-l",NULL};
            
        if (execvp(args[0],args)<0){
            if (debugMode ==1)
                perror("ls -l failed ");
            exit(1);
        }
    }
    // 直接创建第二个子进程,无需等待第一个结束
    int child2 = fork();
    if (child2 == 0)
    {
        if (debugMode == 1)
            fprintf(stderr, "Child 2 is redirecting stdin to read end of pipe.\n");
        // 关闭不需要的写端
        close(p[1]);
        // 关闭标准输入
        close(0);
        // 把管道读端复制到fd 0
        dup2(p[0], 0);
        // 关闭原管道读端
        close(p[0]);
        char *args[] = {"tail","-n","2",NULL};
        if (execvp(args[0],args)<0){
            if (debugMode ==1)
                perror("tail -n 2 failed ");
            exit(1);
        }
    }
    // 父进程逻辑
    if (debugMode == 1)
        fprintf(stderr, "Parent process is closing both ends of pipe.\n");
    // 父进程关闭自己持有的管道两端,必须操作否则管道无法正常终止
    close(p[0]);
    close(p[1]);
    // 等待两个子进程执行完成
    waitpid(child1, &status, 0);
    waitpid(child2, &status, 0);
    if (debugMode == 1)
        fprintf(stderr, "All child processes terminated.\n");
    return 0;
}

额外说明

上述实现完全符合关闭标准输入输出的作业要求:两个子进程分别显式关闭了标准输出fd 1和标准输入fd 0,再将管道端映射到对应fd位置,完成命令的管道级联执行。

内容的提问来源于stack exchange,提问作者OmerLerner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 01:06:02