使用popen与poll读取进程输出时feof不触发如何避免超时等待
根因说明
管道的读端只有在所有关联的写端都被关闭后才会触发EOF返回。你遇到的阻塞问题本质是ssh fork出的后台子进程继承了原父进程持有的管道写端,原父进程退出后子进程还持有该写端未释放,因此popen对应的读端始终无法收到EOF,只能等到poll超时才会终止读取。
可行解决方案
- 方案1:修改ssh启动参数,主动切断后台子进程与管道的关联
ssh本身提供了适配后台运行的参数,你可以在原ssh命令中加入-f+-n参数,同时手动重定向所有标准IO到/dev/null。修改后的命令示例:ssh -f -n -R 本地端口:127.0.0.1:目标端口 用户名@远程主机 >/dev/null 2>&1-f会让ssh在完成认证、隧道建立逻辑后才转入后台,-n会将标准输入重定向到/dev/null,末尾的重定向规则会确保ssh的子进程启动后就关闭标准输出、标准错误的文件描述符,不会持有管道写端,原父进程退出后管道读端就能立刻收到EOF。 - 方案2:自定义进程启动逻辑,避免子进程继承文件描述符
不使用默认的popen,改为自己通过fork/exec创建子进程,在fork之后、exec执行ssh命令之前,给所有需要关闭的文件描述符设置FD_CLOEXEC标记,或者直接关闭除标准输入输出之外的所有多余描述符,确保后台子进程不会持有管道的写端。 - 方案3:同步监听原父进程退出状态,主动终止读取
如果无法修改ssh启动参数,可以在poll监听管道事件的同时,用带WNOHANG参数的waitpid非阻塞轮询原ssh父进程的运行状态,一旦检测到原父进程退出,无论管道是否收到EOF,都直接主动关闭读端、终止读取逻辑,无需等待超时。
内容的提问来源于stack exchange,提问作者Lonko
相关产品推荐
相关产品推荐

