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

在C语言中实现管道时Shell挂起,无法执行ps -j | more命令

我来帮你分析下代码里的问题,以及怎么修复这个挂起的情况:

核心问题分析

你的代码出现挂起主要有两个关键原因:

1. 父进程未及时关闭管道的两端

当创建管道后,父进程、两个子进程都会持有管道读写两端的文件描述符。ps子进程只需要写管道,more子进程只需要读管道,但父进程必须关闭管道的所有两端——如果父进程不关闭,more会一直认为还有进程可能往管道里写数据,就会持续阻塞等待输入,永远不会退出,导致程序挂起。

2. 等待子进程的次数不足

你只循环等待了1次子进程退出,但实际fork了两个子进程,这会导致其中一个子进程变成僵尸进程,同时父进程可能提前退出,进一步引发终端状态混乱,间接加剧挂起问题。


修复后的完整代码

#define BUF_SIZE 100
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main() {
    pid_t pid1, pid2;
    int fd[2];
    char *argv1[] = { "/bin/ps", "-j", NULL };
    char *argv2[] = { "/bin/more", NULL };
    char prompt[] = "Press enter: ";
    char buffer[BUF_SIZE];

    // 保留你原有的用户输入逻辑
    write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);
    ssize_t readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
    if (readIn > 0) {
        buffer[readIn - 1] = '\0';
    }
    printf("readIn: %zd\n", readIn);

    // 创建管道并检查错误
    if (pipe(fd) == -1) {
        perror("pipe failed");
        exit(EXIT_FAILURE);
    }

    // 第一个子进程:执行ps -j
    pid1 = fork();
    if (pid1 == 0) { 
        // 重定向stdout到管道写端
        dup2(fd[1], STDOUT_FILENO);
        // 关闭所有未使用的管道描述符
        close(fd[0]);
        close(fd[1]);
        // 执行ps命令,失败则打印错误
        execvp(argv1[0], argv1);
        perror("execvp ps failed");
        exit(EXIT_FAILURE);
    } else if (pid1 == -1) {
        perror("fork ps failed");
        exit(EXIT_FAILURE);
    }

    // 第二个子进程:执行more
    pid2 = fork();
    if (pid2 == 0) { 
        // 重定向stdin到管道读端
        dup2(fd[0], STDIN_FILENO);
        // 关闭所有未使用的管道描述符
        close(fd[0]);
        close(fd[1]);
        // 执行more命令,失败则打印错误
        execvp(argv2[0], argv2);
        perror("execvp more failed");
        exit(EXIT_FAILURE);
    } else if (pid2 == -1) {
        perror("fork more failed");
        exit(EXIT_FAILURE);
    }

    // 父进程必须关闭管道两端!这是解决挂起的关键
    close(fd[0]);
    close(fd[1]);

    // 等待两个子进程全部结束,避免僵尸进程
    int status;
    waitpid(pid1, &status, 0);
    waitpid(pid2, &status, 0);

    printf("finished\n");
    exit(EXIT_SUCCESS);
}

关键改动说明

  • 父进程关闭管道两端:确保more在ps结束后能收到EOF信号,知道输入已结束,不会无限阻塞。
  • 完整等待子进程:用waitpid分别等待两个子进程,或者也可以用while(wait(NULL) != -1)循环等待所有子进程退出,彻底避免僵尸进程问题。
  • 增加错误处理:对pipe、fork、execvp的返回值做检查,方便调试定位问题。
  • 子进程清理管道描述符:子进程在重定向后,关闭所有未使用的管道描述符,避免不必要的资源泄漏。

这样修改后,程序就能正确实现ps -j | more的管道功能,不会再出现挂起的情况了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:31:33