迷你Shell管道函数复刻问题:进程挂起与执行顺序异常
解决Minishell管道命令的两类异常问题
问题1:echo hi | wc执行后进程挂起
原因分析
- 多余进程层级:
do_pipe_forks已为每个管道命令创建子进程,但execve_commands_pipes中又额外fork了一次,生成了孙子进程。原shell父进程未等待这些孙子进程结束,导致终端出现假挂起,需输入命令触发进程清理才会恢复正常。 - 父进程未等待所有子进程:
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
相关产品推荐
相关产品推荐

