父母和预分叉儿童之间的文件描述符共享

时间:2010-02-21 12:36:46

标签: linux unix sockets network-programming

在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 之后由父创建的文件描述符最终是如何与子进程共享的?

2 个答案:

答案 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>