将Unix域套接字作为STDIN传递至子进程后无法使用的问题排查
维护老旧代码库时,通过以下代码创建Unix域套接字并将其作为STDIN传递给子进程:
int listenFD = socket(AF_UNIX, SOCK_STREAM, 0); ... if( dup2(listenFD, STDIN_FILENO) != 0 ) { warn("ChildProc", "Could not listen on socket fd %d, " "dup2() failed: %d (%s)", listenFD, errno, strerror(errno)); return; }
主进程日志显示套接字创建成功,文件路径符合特征:
/logs% ll /var/my.sock srw------- 1 nobody nobody 0 Jun 21 10:30 /var/my.sock
但子进程无法使用该套接字,报错:
The FastCGI domain socket is not open. Ensure the parent of this process has opened the domain socket.
已确认套接字已打开,询问是否存在遗漏环节。
未完成套接字的
bind()和listen()操作
代码仅展示了socket()调用,但Unix域套接字需要先通过bind()关联到文件路径,再调用listen()进入监听状态才能被使用。如果主进程跳过了这两步,即使套接字文件存在,也只是一个空的文件节点,实际套接字并未处于可接收连接的状态。
排查:检查主进程代码中是否存在bind(listenFD, (struct sockaddr*)&unix_addr, sizeof(unix_addr))和listen(listenFD, backlog)的调用,且返回值无错误。文件描述符的
FD_CLOEXEC标志未清除
如果listenFD被设置了FD_CLOEXEC标志,当子进程通过exec()启动时,该文件描述符会被自动关闭,导致子进程的STDIN实际是关闭状态。
排查:在dup2()之前,调用int flags = fcntl(listenFD, F_GETFD);,若flags & FD_CLOEXEC为真,说明需要清除该标志:fcntl(listenFD, F_SETFD, flags & ~FD_CLOEXEC);子进程启动时重写了STDIN
部分FastCGI实现可能在初始化阶段默认重新打开STDIN(比如绑定到终端或/dev/null),覆盖了主进程通过dup2()传递的套接字。
排查:在子进程启动初期添加调试代码,检查STDIN的状态:struct stat sb; if (fstat(STDIN_FILENO, &sb) == 0 && S_ISSOCK(sb.st_mode)) { // 确认是套接字 } else { // STDIN已被重写 }若发现被重写,需修改子进程代码,保留继承的STDIN套接字。
套接字文件的权限与子进程运行用户不匹配
虽然套接字文件权限为srw-------,但如果子进程是以非nobody用户运行,老旧系统可能存在权限限制(FD继承的套接字通常不受文件路径权限限制,但部分场景仍可能受影响)。
排查:确认子进程的有效用户/组是否与套接字文件的属主(nobody:nobody)一致,若不一致,可调整套接字文件的权限或子进程的运行用户。主进程在
dup2()后意外关闭了套接字
若主进程在fork()子进程后,错误关闭了listenFD或STDIN_FILENO,且未确保子进程已完成初始化,可能导致套接字被提前关闭(FD继承后引用计数会增加,但极端情况下若主进程关闭所有相关FD且子进程尚未完成使用,会导致套接字失效)。
排查:检查主进程在fork()后的代码逻辑,确保子进程启动完成后再处理原套接字FD的关闭操作。
内容的提问来源于stack exchange,提问作者g.d.d.c

