【问题标题】:File Descriptor Sharing between Parent and Pre-forked Children父级和预分叉子级之间的文件描述符共享
【发布时间】:2011-01-19 08:05:19
【问题描述】:

在 Unix 网络编程中有一个 Pre-forked 服务器的示例,它使用 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。让我陷入循环的事情是,因为这些孩子是预先分叉的,我会假设只有在孩子分叉时存在的文件描述符才会被共享。但是,如果这是真的,那么这个例子会非常失败,但它确实有效。

有人能解释一下父进程在分叉后创建的文件描述符是如何最终与子进程共享的吗?

【问题讨论】:

    标签: linux unix sockets network-programming


    【解决方案1】:

    看看 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 类型的控制消息是 unix 可以与不相关的进程共享文件描述符的一种方式。

    【讨论】:

      【解决方案2】:

      您可以使用 Unix 套接字中的 FD 传递机制将(大多数)任意文件描述符发送到可能不相关的进程。

      这通常是一种很少使用的机制,而且很难做到正确 - 两个进程都需要合作。

      大多数 prefork 服务器不这样做,而是让子进程在共享侦听套接字上调用 accept(),并以这种方式创建自己的连接套接字。其他进程看不到这个连接的socket,而且只有一个副本,所以当孩子关闭它时,它就消失了。

      一个缺点是进程无法在调用accept之前告诉客户端要请求什么,因此您无法处理不同子级的不同类型的请求等。一旦一个孩子接受()它,另一个孩子就不能。

      【讨论】:

      • 是的,这些天我真的看不出有多少使用预分叉服务器的理由。我几乎总是会选择预线程服务器。我能想到在哪里运行预分叉服务器的唯一情况可能是出于安全原因,您希望每个客户端的内存空间与任何其他客户端隔离,以便在一个客户端中进行黑客攻击或生成错误不会取消整个系统或暴露敏感的用户数据。
      • 预分叉服务器很好,因为它们可以防止内存泄漏破坏东西。子进程可以在不中断服务的情况下退出和回收,回收内存。
      • 分叉服务器还可以保护您免受 3. 非线程安全的派对内容的影响,并假设只有一个执行线程。周围有很多这样的库。
      猜你喜欢
      • 1970-01-01
      • 2013-02-26
      • 2014-02-26
      • 1970-01-01
      • 2012-12-03
      • 1970-01-01
      • 2014-02-06
      • 1970-01-01
      • 2011-03-11
      相关资源
      最近更新 更多