自定义Minishell中cat | cat | ls管道异常问题求助
Minishell管道异常问题:
cat | cat | ls中cat未等待标准输入 问题场景
我和伙伴开发了迷你Shell(Minishell),负责解析与执行模块。大部分功能运行正常,但执行cat | cat | ls命令时,ls输出正常,但cat未按预期等待标准输入就直接返回提示符。怀疑是管道未在正确时机关闭,但无法定位具体位置。
核心代码
void executing_commands(t_minish *data) { t_cmd *cmd; int process_status; process_status = 0; cmd = data->cmds; if (!cmd->full_cmd) return ; creating_pipes(data); while (cmd && cmd->full_cmd) cmd = creating_child(&cmd, data); closing_all_fd(data); cmd = data->cmds; while (cmd) { waitpid(data->child, &process_status, 0); if (data->cmds && !check_parent_builtin(&cmd)) g_status = WEXITSTATUS(process_status); cmd = cmd->next; } } t_cmd *creating_child(t_cmd **cmd, t_minish *data) { int pid; if (check_parent_builtin(cmd)) executing_builtin(data, cmd); else { pid = fork(); if (ft_memcmp((*cmd)->full_cmd[0], "minishell", 10)) set_signals(EXEC); data->child = pid; if (pid == -1) { closing_all_fd(data); error_manager(2, data, NULL); } else if (pid == 0) child_process(data, cmd); } return ((*cmd)->next); } void child_process(t_minish *data, t_cmd **cmd) { switching_input_output(data, cmd); closing_all_fd(data); if (check_child_builtin(cmd)) executing_builtin(data, cmd); else launching_command(data, cmd); } void switching_input_output(t_minish *data, t_cmd **cmd) { if ((*cmd)->output > 1) { if (dup2((*cmd)->output, STDOUT_FILENO) < 0) error_manager(6, data, cmd); close((*cmd)->output); } if ((*cmd)->input) { if (dup2((*cmd)->input, STDIN_FILENO) < 0) error_manager(6, data, cmd); close((*cmd)->input); } if ((*cmd)->file_in) { if (dup2((*cmd)->file_in, STDIN_FILENO) < 0) error_manager(6, data, cmd); close((*cmd)->file_in); } if ((*cmd)->file_out) { if (dup2((*cmd)->file_out, STDOUT_FILENO) < 0) error_manager(6, data, cmd); close((*cmd)->file_out); } } void launching_command(t_minish *data, t_cmd **cmd) { if (execve((*cmd)->full_path, (*cmd)->full_cmd, data->env_table) == -1) error_manager(3, data, cmd); } void closing_all_fd(t_minish *data) { t_cmd *cmd; cmd = data->cmds; while (cmd) { if (cmd->input) close(cmd->input); if (cmd->output > 1) close(cmd->output); cmd = cmd->next; } } void closing_fd_if_redirections(t_minish *data) { t_cmd *cmd; cmd = data->cmds; while (cmd) { if (cmd->file_in && cmd->input) close(cmd->input); cmd = cmd->next; } } void creating_pipes(t_minish *data) { int fd[2]; t_cmd *cmd; cmd = data->cmds; while (cmd->next != NULL) { if (pipe(fd) == -1) error_manager(1, data, NULL); cmd->output = fd[1]; cmd->next->input = fd[0]; if (!cmd->next->next) cmd->next->last = 1; cmd = cmd->next; } closing_fd_if_redirections(data); }
问题根源
核心问题是父进程未及时关闭管道的写端,导致子进程的管道读端无法触发EOF:
- 执行
cat | cat | ls时,第一个cat的输出连到管道1写端,第二个cat的输入连管道1读端、输出连管道2写端,ls的输入连管道2读端。 - 父进程在创建完所有子进程后才调用
closing_all_fd关闭管道,此时父进程仍持有所有管道的写端引用。 - 第二个
cat的输出管道(管道2)会在ls执行完毕后关闭读端,导致第二个cat收到SIGPIPE退出;同时父进程仍持有管道1的写端,第一个cat的读端(终端stdin)未被阻塞,反而因为下游管道异常直接退出,不会等待用户输入。
修复方案
1. 父进程创建子进程后立即关闭无用管道
在creating_child函数的父进程分支(pid > 0)中,关闭当前命令的输出管道和下一个命令的输入管道——父进程不需要继续持有这些描述符:
else // pid > 0,父进程逻辑 { // 关闭当前cmd的输出管道(子进程已继承并使用) if ((*cmd)->output > 1) close((*cmd)->output); // 关闭下一个cmd的输入管道(子进程已继承并使用) if ((*cmd)->next && (*cmd)->next->input) close((*cmd)->next->input); }
2. 移除父进程最后统一关闭管道的逻辑
删除executing_commands函数中的closing_all_fd(data)调用,因为管道已经在创建子进程时逐步关闭,避免父进程长时间持有管道写端。
3. 验证子进程管道关闭逻辑
当前子进程中调用closing_all_fd是正确的,确保子进程只保留自己需要的标准输入输出描述符,其他管道都被关闭。
内容的提问来源于stack exchange,提问作者bl000m
相关产品推荐
相关产品推荐

