Converting the comment stream into an answer.
TL; DR
是的。问题中的描述看起来正确并且推理合理。
在某些时候,您的父进程应该等待已死亡的子进程,以防止僵尸进程的累积(但在子进程死亡之前它不应该阻塞)。使用waitpid() http://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html循环中WNOHANG
在父级关闭循环的部分中,参数可能是合适的new_socket_fd
。这可能会留下一个或多个僵尸,直到发出下一个传入请求。如果这是一个问题,你可以忽略SIGCHLD
(因此永远不会创建僵尸),或者您可以安排定期唤醒,在此期间父进程检查僵尸。
讨论
babon https://stackoverflow.com/users/4845636/babon asked https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77351441_45190328
快速问题 - 那么父进程何时/在何处关闭 socket_fd?
父级关闭socket_fd
当它退出循环或被告知停止侦听套接字时。所示代码中没有对此进行真正的规定,因此当父进程被终止(或发生分叉失败)时它将被关闭。重点是侦听套接字可用于许多连接 - 在完成侦听之前,您不想在父级中关闭它。
Matteo https://stackoverflow.com/users/5238305/matteo noted https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77351554_45190328
在这种情况下,因为它是一个无限循环,所以永远不会。服务器将始终监听最多 N 个定义的连接listen(socket_fd, N)
.
请注意,N 参数listen()
call 是可以排队等候侦听进程的未完成连接数。即尚未通过连接返回值的连接请求数accept()
称呼。它不是对可以同时活动的连接数的限制accept()
已接受连接。
阿贾伊·布拉马克沙特里亚 https://stackoverflow.com/users/2858773/ajay-brahmakshatriya asked https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77351472_45190328
在孩子关闭之前socket_fd
,绑定端口是否映射到两个 PID?如果有传入的数据包,它会被放入谁的队列中?
传入数据包与套接字的“打开文件描述”(或等效内容 - 与“文件描述符”或“套接字描述符”不同)相关联。它可供父母或孩子使用,以先读者为准。同样,传入的连接请求也会在socket_fd
;它们可以被父母或孩子接受。然而,家人已经就谁该做什么达成了一致,这样他们就不会互相妨碍。
Matteo 评论了 https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77351585_45190328
我想是给父母的。
Ajay 回应了 https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77351665_45190328
如果是这种情况,同样的情况也应该发生在数据包上new_socket_fd
因为两者都打开了。这意味着在父级关闭数据包之前,子级将无法读取数据包。这可能会导致竞争条件。
这是基于一个误解。该数据包可通过文件描述符供两个进程使用。当进程关闭文件描述符时,它无法再访问发送到连接的信息(当然,也无法在该连接上发送数据)。在那之前,谁能看到什么就像一场抽签,除非参与者进程就哪个读取数据、哪个监听连接达成一致
Matteo 回应了 https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77352107_45190328
但文件描述符不应该干扰父子之间的关系;这就是为什么关闭socket_fd
孩子这边并不能阻止父母倾听。
babon 评论了 https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77352171_45190328
同意。但我认为你应该关闭socket_fd
在 while 循环之后。如果明天您更改循环以在某些情况下中断,您将面临无缘无故保持打开套接字的风险。
这是一个很好的做法,但是循环不会退出(它是一个while (1)
循环,并且故障模式会执行return
退出循环 - 它可以在执行此操作之前关闭套接字return
)。如果程序退出,那么系统将关闭套接字,因此这并不重要,尽管关闭您打开的内容是很好的内务处理。
Ajay notes https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77352301_45190328
父级和子级中的文件描述符不同。因此关闭一个不应影响另一个。但是两个副本都具有相同的(src-ip、src-port、dest-ip、dest-port)4 元组,那么具有此类标头的数据包会去哪里?
描述符不同,但它们引用的套接字连接是相同的。该数据包可供任何读取它的进程(父进程或子进程)使用。
Matteo 回应了 https://stackoverflow.com/questions/45190328/is-this-the-correct-way-to-close-socket-descriptors-on-fork?noredirect=1#comment77352650_45190328
在我的例子中accept_client
创造了sockaddr
结构体为客户端,所以 4 元组去孩子的new_socket_fd
.
这不太准确。第一的,accept_client()
在有孩子之前被调用;这new_socket_fd
(仅)当该函数完成时位于父级中。其次,之后fork()
,两个进程都可以访问new_socket_fd
,并且都可以读取客户端进程发送的数据。然而,该程序的设计是为了让服务器在子进程处理传入连接时返回侦听更多连接请求。new_socket_fd
——合理的分工。
请注意,允许父进程处理请求而子进程继续监听。然而,这有悖于惯例。这意味着监听的“守护进程”进程会更改每个连接请求的 PID,从而很难确定当前正在监听套接字的进程。代码中使用的传统方法是守护进程在很长一段时间内保持不变,因此记录 PID 以供以后的进程控制(当不再需要时杀死守护进程)是明智的。