1

情况:

  • 多个进程共享同一个文件描述符表。
  • 每个进程都监听自己的 epoll 实例。
  • 所有涉及它们的套接字和调用都是非阻塞的。
  • 在这些进程中,只有进程 A 将监听套接字添加到其 epoll 实例中。
  • 进程A知道所有其他进程的epolls的fds。
  • 当一个新的套接字(即一个新的连接)到达时,进程 A 将它添加到其他进程之一的 epoll 实例中......

...像这样:

int new_sfd;
while ((new_sfd = accept4(listening_fd, NULL, NULL, SOCK_NONBLOCK)) != -1) {
    if (epoll_ctl(other_epoll_fds[new_sfd % PROCESS_C], EPOLL_CTL_ADD, new_sfd,
                  &(struct epoll_event){
                      .data = {.fd = new_sfd},
                      .events = EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET
                  }) == -1) {
        perror("Failed to add a new socket to an epoll instance");
        close(new_sfd);
    }
}
if (errno != EAGAIN) {
    perror("Failed to accept one or more incoming connections");
}

这似乎有效(即在这个阶段没有发生错误)。当一个连接进入进程 A 时,它被添加到进程 B 的 epoll 中,之后进程 B 得到一个设置了 EPOLLIN 标志的事件,正如预期的那样。然后进程B通过读取接收到的epoll_event结构的data.fd成员来获取新socket的fd,并recv()在该fd上尝试a。

事情出乎意料地出错了。recv()返回 -1 并出现以下错误:Socket operation on non-socket.

是什么赋予了?通过在各处插入大量调试printf()语句,我彻底验证了accept4()进程 A 返回的 fd 值实际上与我作为第一个参数传递给recv()进程 B 的 fd 值相同(同样,所有进程共享相同的文件描述符表),所以我无法理解这一点。帮助?!丁:

4

2 回答 2

2

很多谷歌搜索后,我已经登陆以下页面:

https://patchwork.kernel.org/patch/2356101/

显然execve()撤消了我设置的文件描述符表clone()CLONE_FILES标志的共享(因此调用之后出现的任何套接字 fdexecve()都会导致侦听进程 A 的表的写时复制,从而使更改对另一个不可见过程)。

我很不幸,execve()当前版本的手册中还没有记录这种行为。因此,Kevin Easton 提供了上述补丁(感谢 Kevin)。另外,感谢Devolus引导我朝着正确的方向前进。

于 2013-06-04T07:22:04.377 回答
1

如果 A 和 B 真的是两个独立的进程,那么它们有一组不同的文件描述符。所以 A 中的描述符 6 不一定与 B 中的 6 相同。如果返回值表明 FD 不是套接字,那么它可能不是,但这在您的代码中不可见。使用 printf 验证您传递相同的 FD 仅确保这些 FD 具有相同的值,但这并不意味着它们是相同的,因为它们很可能不是。

于 2013-06-04T05:29:09.313 回答