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

使用pipe()和dup2()模拟Linux Shell管道时程序停滞的问题

管道操作停滞无输出的问题修复(不修改do_command结构)

问题原因

程序停滞无输出的核心是主进程持有管道写端的额外未关闭引用:

  1. 主进程调用dup2(pipefd[1], STDOUT_FILENO)后,自身标准输出被绑定到管道写端,但主进程并未关闭这个副本,也没有退出。
  2. 执行echo的子进程退出后,主进程关闭了管道写端的原描述符pipefd[1],但主进程的STDOUT_FILENO仍是管道写端的副本,处于打开状态。
  3. 执行rev的子进程从管道读端读取数据时,因为管道写端还有主进程的引用未关闭,无法收到EOF信号,会一直阻塞等待输入,导致程序停滞。

修复方案(不修改do_command结构)

不要在主进程中直接修改自身文件描述符,而是为每个命令单独创建二级子进程,在二级子进程内完成管道重定向,确保主进程不持有管道的任何引用。具体步骤:

  • 创建管道后,fork子进程专门处理echo:在该子进程内完成stdout到管道写端的重定向,关闭管道未使用描述符后调用do_command。
  • 主进程关闭管道写端,再fork子进程处理rev:在该子进程内完成stdin到管道读端的重定向,关闭管道读端后调用do_command。
  • 主进程关闭管道读端,等待所有子进程退出。

修复后的代码

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

int do_command(char* args[])
{
    int status;
    pid_t pid = fork();

    if (pid == 0)
    {
        execvp(args[0], args);
        // execvp失败才会执行到这里
        perror("execvp");
        exit(EXIT_FAILURE);
    }
    else if (pid == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else
    {
        wait(&status);
    }

    return 0;
}

int main() {
    int pipefd[2];
    pid_t pid_echo, pid_rev;

    // 创建管道
    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    // 处理echo命令的子进程
    pid_echo = fork();
    if (pid_echo == 0) {
        // 重定向stdout到管道写端
        if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
            perror("dup2");
            exit(EXIT_FAILURE);
        }
        // 关闭管道的原始描述符(已通过dup2获得副本)
        close(pipefd[0]);
        close(pipefd[1]);

        char* echo[] = { "echo", "Hello, World!", NULL };
        do_command(echo);
        exit(EXIT_SUCCESS);
    } else if (pid_echo == -1) {
        perror("fork echo");
        exit(EXIT_FAILURE);
    }

    // 主进程关闭管道写端
    close(pipefd[1]);

    // 处理rev命令的子进程
    pid_rev = fork();
    if (pid_rev == 0) {
        // 重定向stdin到管道读端
        if (dup2(pipefd[0], STDIN_FILENO) == -1) {
            perror("dup2");
            exit(EXIT_FAILURE);
        }
        // 关闭管道读端
        close(pipefd[0]);

        char* rev[] = { "rev", NULL };
        do_command(rev);
        exit(EXIT_SUCCESS);
    } else if (pid_rev == -1) {
        perror("fork rev");
        exit(EXIT_FAILURE);
    }

    // 主进程关闭管道读端
    close(pipefd[0]);

    // 等待两个子进程执行完成
    waitpid(pid_echo, NULL, 0);
    waitpid(pid_rev, NULL, 0);

    return 0;
}

扩展说明

  • 修复后程序会正常输出反转后的字符串!dlroW ,olleH,因为echo子进程退出后,管道写端的所有引用都会被关闭,rev子进程读取完数据后会收到EOF并正常退出。
  • 保留了do_command的原有结构,后续扩展多管道链时,只需重复"创建管道→fork子进程处理前序命令→关闭对应管道端→fork子进程处理后续命令"的逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:14:59