在Unix网络编程中有一个预分叉服务器的例子,该服务器使用Unix域管道上的消息传递来指示子进程处理传入连接:
for ( ; ; ) {
rset = masterset;
if (navail <= 0)
FD_CLR(listenfd, &rset); /* turn off if no available children */
nsel = Select(maxfd + 1, &rset, NULL, NULL, NULL);
/* 4check for new connections */
if (FD_ISSET(listenfd, &rset)) {
clilen = addrlen;
connfd = Accept(listenfd, cliaddr, &clilen);
for (i = 0; i < nchildren; i++)
if (cptr[i].child_status == 0)
break; /* available */
if (i == nchildren)
err_quit("no available children");
cptr[i].child_status = 1; /* mark child as busy */
cptr[i].child_count++;
navail--;
n = Write_fd(cptr[i].child_pipefd, "", 1, connfd);
Close(connfd);
if (--nsel == 0)
continue; /* all done with select() results */
}
如您所见,父级将套接字的文件描述符编号写入管道,然后在文件描述符上调用close。当preforked孩子完成套接字时,他们也会在描述符上调用close。抛弃我循环的事情是,因为这些孩子是预先伪装的,我会假设只有孩子被分叉时存在的文件描述符才会被共享。但是,如果这是真的,那么这个例子就会失败,但它确实有效。
有人能否了解一下在fork 之后由父创建的文件描述符最终是如何与子进程共享的?
答案 0 :(得分:5)
看看Write_fd实现。它使用类似
的东西union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA(cmptr)) = sendfd;
也就是说,发送类型为SCM_RIGHTS的控制消息是unixes可以与未发布的进程共享文件描述符的方式。
答案 1 :(得分:1)
您可以使用Unix套接字中的FD传递机制将(大多数)任意文件描述符发送到可能不相关的进程。
这通常是一种很少使用的机制,而且很难做到 - 两个流程都需要合作。
大多数prefork服务器不会这样做,而是在共享侦听套接字上有子进程调用accept(),并以这种方式创建自己的连接套接字。其他进程看不到这个连接的套接字,并且只有一个副本,所以当孩子关闭它时,它就消失了。
一个缺点是,在调用accept之前,进程无法告诉客户端要求的内容,因此您无法在不同的子进程中处理不同类型的请求等。一旦一个子进行了accept()编辑,另一个子进程就不能。< / p>