Golang中SocketPair与exec.Run的IO竞态条件根因问询
问题根源:SocketPair管道与子进程STDIN的EOF竞态
核心原因
问题的本质在于UNIX域套接字(由SocketPair创建)的EOF触发规则:只有当该套接字的所有写入方向的文件描述符都被完全关闭时,读取端才会收到EOF信号(即read系统调用返回0)。
场景拆解
导致进程挂起的流程
当你先创建子进程(cat),再启动协程写入数据时:
- 子进程启动后,持有套接字读端的文件描述符,开始阻塞等待数据。
- 协程启动,将
os.Stdin的数据写入套接字写端,完成写入后协程退出,但主进程仍然持有写端的打开引用。 - 子进程读完所有传输的数据后,发现套接字的写端还有未关闭的文件描述符(主进程持有的),因此会一直阻塞在读取操作上,等待更多数据或EOF,最终导致进程挂起。
临时解决方法生效的原因
- 将协程移至exec.Command创建前:协程先完成数据写入,并关闭写端(或主进程在创建子进程前主动关闭写端)。子进程启动后读取数据,读完时所有写端已关闭,立即收到EOF,正常退出。
- 用通道确保协程先启动:本质是强制让协程先完成写入+关闭写端的动作,再启动子进程,避免了子进程读完数据后写端仍未关闭的情况。
- 复杂场景下用time.Sleep:通过延迟子进程启动,给协程足够时间完成写入并关闭写端,间接满足了“写端先于子进程读完数据前关闭”的条件。
为什么读取启动顺序会影响结果?
不是“谁先读取”本身有影响,而是写端的关闭时机与子进程读取完成时机的相对关系决定了结果:
- 如果子进程先启动读取,且读完数据后写端仍未完全关闭,子进程会一直等待EOF;
- 如果写端在子进程读完数据前就已完全关闭,子进程读完数据后立即收到EOF,正常退出。
内容的提问来源于stack exchange,提问作者404
相关产品推荐
相关产品推荐

