自定义Shell开发:dup2()致子进程在execvp()前终止的问题排查
问题分析与解决方案
核心问题1:不必要的管道重定向引发阻塞
你当前的代码无论命令是否需要管道,都会强制将子进程的标准输出重定向到管道。比如执行sort input.txt时,sort的输出会被送到管道写端,但父进程只是等待子进程结束,并没有读取管道读端的内容。当管道缓冲区被填满后,子进程会因为无法继续写入而阻塞,看起来像是子进程提前终止,实际上是卡在了输出步骤(比如printf或execvp执行后的命令输出)。
核心问题2:RunSubCommand中的数组越界错误
在RunSubCommand的打印循环里,你错误地使用了args[i]而不是args[j]:
for(j=0;j<i;j++) { printf("%s\n", args[i]); // 这里应该是args[j] }
i已经是数组的末尾索引(指向NULL),访问args[i]会读取空指针,直接触发段错误导致子进程崩溃,自然无法执行到execvp。
修复后的代码示例
1. 区分管道场景与普通命令场景
只有当处理管道链中的命令时(比如前一个命令的输出作为后一个的输入),才创建管道并进行重定向。对于普通命令,跳过管道重定向逻辑:
// 示例:假设你有一个标志位is_piped,标记当前命令是否属于管道链 int is_piped = 0; // 根据实际解析逻辑设置,比如检测到|时设为1 int f2[2]; if (is_piped) { pipe(f2); } int retval2 = fork(); if(retval2 == 0) { printf("IF statement\n"); if(input_direct != STDIN_FILENO) { dup2(input_direct, STDIN_FILENO); close(input_direct); } // 仅在管道场景下重定向stdout到管道 if (is_piped) { printf("statement before calling Dup2(f2[1])\n"); dup2(f2[1] , STDOUT_FILENO); close(f2[0]); close(f2[1]); } printf("statement before calling RSC()\n"); RunSubCommand(token, startindex, endindex); } else if(retval2 > 0) { if (is_piped) { close(f2[1]); wait(NULL); input_direct = f2[0]; } else { wait(NULL); // 普通命令无需保留管道读端,清空input_direct或做对应处理 } }
2. 修复RunSubCommand的数组越界问题
void RunSubCommand(char ** token, int startindex, int endindex) { printf("RunSubCommand Fucntion Call: \n"); char * args[(endindex - startindex)+1]; int i=0,j=0, end, start; start = startindex; end = endindex - startindex; while(i < end) { args[i] = token[start]; i++; start++; } args[i] = NULL; printf("Printing args: \n"); for(j=0;j<i;j++) { printf("%s\n", args[j]); // 修复为args[j] } execvp(args[0], args); perror("Error in executing EXECVP"); }
额外优化建议
- 关闭未使用的文件描述符:父进程后续若不再使用
input_direct,需手动关闭,避免资源泄漏;子进程中管道相关的描述符要确保全部关闭。 - 规范错误提示:
perror参数无需添加换行,它会自动追加,改成perror("Error in executing EXECVP")即可。
内容的提问来源于stack exchange,提问作者wiwi
相关产品推荐
相关产品推荐

