C语言实现多进程管道时遇阻塞及execvp执行失败问题求助
多管道Shell实现问题排查
问题描述
用C语言编写Shell,尝试实现多管道功能:通过二维管道数组为每个管道分配独立结构,解析函数拆分管道间命令并存入结构体,每个命令对应独立进程。单管道场景运行正常,但多管道时无输出且程序卡住,GDB调试显示第二个进程fork后execvp执行失败。
代码实现
int create_pipe(int* fd) { int pipe_id = pipe(fd); if (pipe_id == -1) { return -1; } return 0; } void write_pipe(int* fd) { close(fd[READ]); if ((dup2(fd[WRITE], STDOUT_FILENO)) < -1) { fork_error(); } close(fd[WRITE]); } void read_pipe(int *fd) { close(fd[WRITE]); if (dup2(fd[READ], STDIN_FILENO) < 0) { fork_error(); } close(fd[READ]); } void need_to_pipe (int i, int (*fd)[2]) { if (commands[i].pos == first) { write_pipe(fd[i * 2]); } else if (commands[i].pos == last) { read_pipe(fd[(i-1) *2]); } else //if (commands[i].pos == middle) { dup2(fd[(i-1)*2][READ], STDIN_FILENO); close(fd[(i-1)*2][READ]); close(fd[(i-1)*2][WRITE]); close(fd[(i)*2][READ]); dup2(fd[i*2][WRITE], STDOUT_FILENO); close(fd[i*2][WRITE]); } } void fork_cmd(int i, int (*fd)[2]) { pid_t pid; switch (pid = fork()) { case -1: fork_error(); case 0: if (!(commands[i].pos == single)) { need_to_pipe(i, fd); } if (execvp(commands[i].argv[0], commands[i].argv)<0) { exit(EXIT_FAILURE); } } } void fork_cmds(int n, int (*fd)[2]) { for (int i = 0; i < n; i++) { fork_cmd(i, fd); } } void wait_once () { wait(NULL); } void wait_for_all_cmds(int n) { for (int i = 0; i < n; i++) { wait_once(); //wait for number of child processes. } } int main() { int n; size_t size = 256; char line[size]; while(true) { get_line(line, size); n = parse_cmds(line, commands); int fd[(n-1)][2]; for(int i =0;i<n-1;i++) { int pipe_id = pipe(fd[i*2]); if (pipe_id == -1) { return -1; } } fork_cmds(n, fd); for(int i =0;i<n-1;i++) { int *fdclose= fd[i*2]; close (fdclose[READ]); close (fdclose[WRITE]); } wait_for_all_cmds(n); } exit(EXIT_SUCCESS); }
问题原因分析
1. 管道数组索引严重错误
主函数中定义的管道数组为int fd[(n-1)][2],是包含n-1个管道的二维数组,有效下标为0到n-2。但代码多处错误使用i*2作为索引:
- 创建管道时
pipe(fd[i*2]),当i>=1时会超出数组范围(比如n=3时,i=1访问fd[2]属于越界),导致管道创建到错误内存区域,触发未定义行为。 need_to_pipe函数中,首尾命令和中间命令的管道索引均采用i*2/(i-1)*2,同样越界,子进程拿到错误的文件描述符,无法正确重定向标准IO,最终引发execvp失败或进程阻塞。
2. write_pipe函数的错误判断
write_pipe中对dup2返回值的判断逻辑错误:
if ((dup2(fd[WRITE], STDOUT_FILENO)) < -1)
dup2失败时返回-1,此处判断< -1永远不会成立,导致dup2失败后无法触发错误处理,子进程标准输出重定向失败,后续execvp执行的命令无法正常输出,甚至因文件描述符异常执行失败。
3. 管道文件描述符关闭不规范
- 中间命令分支中,因索引错误,实际关闭的是无效的管道描述符,上游管道的写端未被正确关闭,下游进程会一直等待数据,导致程序卡住。
- 主进程关闭管道时同样因索引错误,关闭的是无效描述符,残留的未关闭管道会导致子进程阻塞。
修复建议
- 修正管道数组索引:所有访问管道数组的地方直接使用
i作为下标,比如创建管道改为pipe(fd[i]),need_to_pipe中首尾命令改为write_pipe(fd[i])、read_pipe(fd[i-1]),中间命令改用fd[i-1]和fd[i]。 - 修正
write_pipe的判断条件:改为if (dup2(fd[WRITE], STDOUT_FILENO) < 0)。 - 确保子进程中所有未使用的管道描述符被正确关闭,避免不必要的阻塞。
内容的提问来源于stack exchange,提问作者user21200173
相关产品推荐
相关产品推荐

