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

迷你Shell管道函数复刻问题:进程挂起与执行顺序异常

解决Minishell管道命令的两类异常问题

问题1:echo hi | wc执行后进程挂起

原因分析

  1. 多余进程层级:do_pipe_forks已为每个管道命令创建子进程,但execve_commands_pipes中又额外fork了一次,生成了孙子进程。原shell父进程未等待这些孙子进程结束,导致终端出现假挂起,需输入命令触发进程清理才会恢复正常。
  2. 父进程未等待所有子进程:do_pipes创建完子进程后,没有等待所有子进程退出,shell提前回到输入状态,但后台仍有未结束的子进程,引发终端异常。

修复步骤

  • 重构execve_commands_pipes,移除内部的fork和waitpid,直接在子进程中执行execve:
    void    execve_commands_pipes(t_token *curr, t_mhstruct *mh)
    {
        char    **arg;
        char    **env;
        char    *path;
    
        path = NULL;
        env = get_env_array(mh);
        arg = get_args_pipes(curr, mh);
        if (check_path(arg[0], env, &path) == 0)
        {
            if (access(arg[0], R_OK) == 0)
                path = arg[0];
            else
            {
                free(env);
                free(arg);
                pr_err(mh, 127, gemsg(mh->emsg[11], mh->emsg[12], arg[0]));
                exit(127);
            }
        }
        execve(path, arg, env);
        // execve失败才会执行以下代码
        perror("execve");
        free(env);
        free(arg);
        exit(1);
    }
    
  • 修改do_pipe_forks返回子进程PID,供父进程等待:
    pid_t    do_pipe_forks(t_mhstruct **mh, int pipes[1000][2], int  i, int  lines, int screen)
    {
        pid_t     pid;
        t_token *curr;
        (void) screen;
        curr = (*mh)->token;
        while (curr && curr->pi != i)
            curr = curr->next;
        pid = fork();
        if (pid == 0)
        {
            set_pipe(curr, pipes, i, lines, screen);
            close_pipes(pipes, lines - 1);
            execve_commands_pipes(curr, *mh);
            exit(1);
        }
        return pid;
    }
    
  • 在do_pipes末尾添加循环,等待所有子进程退出:
    void    do_pipes(t_mhstruct **mh, char **grid, int lines)
    {
        int screen = dup(STDOUT_FILENO);
        int i;
        int pipes[1000][2];
        pid_t pids[1000];
        (void) grid;
        
        i = 0;
        while (i < lines - 1) 
        {
            pipe(pipes[i]);
            i++;
        }
        i = 0;
        while (i < lines)
        {
            pids[i] = do_pipe_forks(mh, pipes, i, lines, screen);
            i++;
        }
        close_pipes(pipes, lines - 1);
        // 等待所有子进程完成
        i = 0;
        while (i < lines)
        {
            waitpid(pids[i], &GLOBAL_ERROR, 0);
            i++;
        }
    }
    

问题2:sleep 6 | cat Makefile | wc串行执行,不符合bash并发逻辑

原因分析

原execve_commands_pipes中的waitpid导致每个管道命令的子进程会等待自己fork的孙子进程执行完毕,使得管道命令变成串行执行(先等sleep结束,再执行cat,最后执行wc)。而bash中管道命令是并发执行的,只要前序进程输出足够数据,后续进程就能先处理完成。

修复步骤

移除execve_commands_pipes中的fork和waitpid逻辑,直接在do_pipe_forks创建的子进程中执行execve,让所有管道命令的子进程同时运行,实现并发逻辑(此修复已包含在问题1的重构中)。

额外管道关闭优化

原close_pipes函数的循环条件存在边界错误,修改为:

void   close_pipes(int pipes[1000][2] , int count)
{
    int i;

    i = 0;
    while (i < count)
    {
        close(pipes[i][0]);
        close(pipes[i][1]);
        i++;
    }
}

调用时传入实际需要关闭的管道数量(lines个命令对应lines-1个管道),避免遗漏或误关。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:47:02