子进程经execv执行bash命令时无法读取管道内容且程序崩溃
程序崩溃、无输出的核心原因
你描述的“不加重定向逻辑就正常执行命令、一加相关代码就在exec附近崩溃”的现象,是代码里多处硬伤叠加导致的,每一个问题都足够单独让程序跑不通:
- 变量名拼写错误,直接触发内存非法访问
你在开头定义的管道数组是pipefd[2],但所有操作管道的位置都写成了pfd——这个变量在代码里完全没有定义,正常编译会直接报未声明标识符的错误。这刚好匹配你遇到的现象:不加重定向逻辑时,代码根本不会访问到pfd,所以命令执行正常;一加上重定向、关闭管道的代码,就要读写这个不存在的变量,在弱编译检查环境下这类访问会指向随机内存地址,直接触发段错误,崩溃位置刚好在execv调用前后。 - 管道创建顺序完全颠倒
你现在的执行流程是先调用fork()创建子进程,再在父子进程里分别调用pipe(pipefd)创建管道。这种写法下,父进程和子进程会各自创建一套完全独立、互不连通的管道,根本没办法在父子进程之间传递数据。正确的顺序必须是先调用pipe创建管道,再执行fork,这样fork出来的父子进程才会继承同一套管道的文件描述符,实现跨进程通信。 - 父进程读写顺序错误,触发死锁
你在父进程的逻辑里是先关闭管道写端、调用waitpid()等子进程完全退出,才开始从管道读数据。管道本身的内核缓冲区大小有限(通常为64KB),如果子进程执行的命令输出量超过缓冲区容量,子进程写管道时会阻塞,等着父进程把缓冲区的数据读走;但此时父进程正卡在waitpid等子进程结束,两边互相等对方动作,直接形成死锁,表现为程序完全没输出、卡在原地不动。 - 管道文件描述符没有完全关闭,导致读取永久阻塞
子进程里通过dup2把标准输出、标准错误重定向到管道写端后,原来的pipefd[1]文件描述符仍然处于打开状态。正常流程里dup2完成后,必须立刻关闭这个多余的管道写端,不然管道写端的引用计数永远不会降到0,父进程从管道读数据时会一直阻塞等着写端全部关闭,哪怕命令已经跑完也永远读不到EOF。 - 管道读取逻辑不合法,触发字符串越界
你用read()把管道里的原始字节读到bufread数组后,直接把它当C字符串传给fprintf用%s打印。read()只会把内核里的原始数据拷到缓冲区,不会自动在字符串末尾补\0结束符,这种操作要么打印出乱码,要么因为访问到缓冲区外的内存直接崩溃。而且你每读一段就手动追加一个\n,哪怕原命令输出本身带换行,也会打出多余的空行。 - exec参数数组长度计算错误,触发栈越界
你初始化init_argv数组时,长度用countWords(buffer)的计算结果做参考,但实际传的参数是固定的3个元素("sh"、"-c"、待执行的命令字符串、NULL结束符),和命令的单词数没有任何关系。如果countWords算出来的长度比实际需要的数组长度小,就会直接在栈上越界写数据,在execv执行前就把栈结构破坏,触发崩溃。 - 完全缺失系统调用的错误检查
代码里对pipe()、fork()、fopen()、execv()这些可能失败的系统调用,完全没有做返回值校验。一旦哪个调用失败(比如管道创建失败、fork失败、文件打不开),程序会顺着错误的逻辑往下跑,既不会输出错误提示,也不会正常完成命令执行流程。
正确执行流程参考
基于管道的命令重定向逻辑,必须按以下顺序操作才能稳定运行:
- 每读取文件里的一行待执行命令后,先调用
pipe()创建管道 - 调用
fork()创建子进程,让子进程继承管道的文件描述符:- 关闭子进程的管道读端
- 用
dup2把标准输出、标准错误重定向到管道写端 - 关闭多余的原管道写端文件描述符
- 调用
exec启动bash执行对应的命令
- 回到父进程:
- 关闭父进程不需要的管道写端
- 循环从管道读端读取数据,边读边打印,直到管道读到EOF
- 最后调用
waitpid回收子进程资源
内容的提问来源于stack exchange,提问作者JavaDumbell
相关产品推荐
相关产品推荐

