考虑以下代码:
socket_fd = start_server(port);
while (1){
new_socket_fd = accept_client(socket_fd);
int pid = fork();
if (pid == 0){
//I am the child server process
close(socket_fd); <------(1)
do_stuff_with_client(new_socket_fd, buffer);
close(new_socket_fd); <------(2)
exit(0);
} else if (pid > 0){
//I am the parent server process
close(new_socket_fd); <------(3)
} else {
fprintf(stderr, "Fork error\n");
return 1;
}
}
根据我的理解,当一个进程调用fork()时,它的地址空间是重复的但不是共享的,所以如果我从子进程中更改变量或关闭文件描述符,它就不会影响父进程 .
也就是说,在服务器接受了一个新连接(从而创建 new_socket_fd
)之后,它会自行分叉,并且子关闭 socket_fd
(1),因为它不是必需的,因为父级仍在监听 socket_fd
.
子进程处理请求,然后关闭 new_socket_fd
(2)并退出 .
当孩子正在完成所有这些操作时,父进程已经关闭 new_socket_fd
(3),因为孩子正在处理连接 .
问题是:这些假设是对的吗?
1 回答
将评论流转换为答案 .
TL; DR
是 . 问题中的描述看起来是正确的,推理是合理的 .
在某些时候,你的父进程应该等待已经死亡的子进程以防止僵尸的积累(但是在孩子死亡之前它不应该阻塞) . 在具有
WNOHANG
参数的循环中使用waitpid()可能是合适的,在父循环关闭new_socket_fd
的循环部分 . 这可能会留下一个或多个僵尸,直到下一个传入请求 . 如果这是一个问题,你可以忽略SIGCHLD
(因此永远不会创建僵尸),或者你可以安排定期唤醒,在此过程中父进程检查僵尸 .讨论
babon asked
当父级退出循环或被告知停止侦听套接字时,父级会关闭
socket_fd
. 有's no real provision for that in the code shown, so it will be closed when the parent process is killed (or a fork failure occurs). The whole point is that the listening socket can be used for many connections — you don'想要在父母中关闭它,直到你听完为止 .Matteo noted
请注意,
listen()
调用中的N参数是可以为侦听进程排队的未完成连接数 . 也就是说,尚未通过accept()
调用返回值的连接请求数 . 在accept()
接受连接后,它不是可以同时激活的连接数的限制 .Ajay Brahmakshatriya asked
传入的数据包与套接字's '打开文件描述符' (or equivalent — as distinct from '文件描述符' or '套接字描述符')相关联 . 它可供父母或孩子使用,无论哪个先读取 . 同样,传入的连接请求在
socket_fd
上排队;他们可以被父母或孩子接受 . 然而,这个家庭已经同意谁做了什么,所以他们不会这样做 .Matteo commented
Ajay responded
这是基于一种误解 . 该数据包可通过文件描述符用于两个进程 . 当进程关闭文件描述符时,它无法再访问发送到连接的信息(或者当然在该连接上发送数据) . 在此之前,除非参与者处理同意哪个读取数据以及哪个人监听连接,否则看到什么是乐透
Matteo responded
babon commented
这是一个很好的做法,但循环不会退出(它是一个
while (1)
循环,并且失败模式在循环之外执行return
- 它可以在执行_2450158之前关闭套接字) . 如果程序退出,那么系统会关闭套接字,所以它并不重要,因为所有这些都是很好的管理来关闭你打开的东西 .Ajay notes
描述符是不同的,但它们引用的套接字连接是相同的 . 该数据包可供任何读取它的进程使用 - 父进程或子进程 .
Matteo responded
这不太准确 . 首先,在有孩子之前调用
accept_client()
;当该函数完成时,new_socket_fd
在父(仅)中 . 其次,在fork()
之后,两个进程都可以访问new_socket_fd
,并且可以读取客户端进程发送的数据 . 但是,该程序的设计使得服务器返回监听更多连接请求,同时子进程在new_socket_fd
上处理传入连接 - 这是一种合理的分工 .请注意,允许父进程处理请求并且子进程继续侦听是允许的 . 但是,这与惯例相反 . 这意味着'守护进程'进程侦听会在每个连接请求上更改PID,这使得很难确定哪个进程当前正在侦听套接字 . 代码中使用的传统方法是守护进程在很长一段时间内保持不变,因此记录PID以便以后进行过程控制(在不再需要时杀死守护进程)是明智的 .