【问题标题】:adding new sockets to epoll instances other than the epoll instance that polls the listening socket向 epoll 实例添加新的套接字,而不是轮询侦听套接字的 epoll 实例
【发布时间】:2013-06-04 04:31:53
【问题描述】:

情况:

  • 多个进程共享同一个文件描述符表。
  • 每个进程都监听自己的 epoll 实例。
  • 所有套接字和涉及它们的调用都是非阻塞的。
  • 在这些进程中,只有进程 A 将侦听套接字添加到其 epoll 实例中。
  • 进程 A 知道所有其他进程的 epoll 的 fd。
  • 当新套接字(即新连接)到达时,进程 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,并在该fd上尝试recv()

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

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

【问题讨论】:

    标签: c linux sockets file-descriptor epoll


    【解决方案1】:

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

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

    显然execve() 撤消了我用clone()CLONE_FILES 标志设置的文件描述符表的共享(因此在execve() 之后出现的任何套接字fd 都会导致写入时复制监听进程 A 的表,使更改对其他进程不可见)。

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

    【讨论】:

    • 有趣的是,你的问题让我意识到我的服务器代码中存在一个相当相似的问题,我不知道,现在可以在它以后甚至导致问题之前解决。 :)
    【解决方案2】:

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

    【讨论】:

    • 不,我在问题中两次提到进程 do 共享相同的文件描述符表。如何?好吧,所有进程都是从同一个父进程产生的,并设置了clone()CLONE_FILES 标志。如果需要,请参阅clone() 的手册页以获取更多信息。
    • 但是......在clone()ing 之后,我确实在子进程中调用了exec()。现在,AFAIK exec() 除了关闭任何设置了 FD_CLOEXEC 标志的 fd 之外,对文件描述符表什么也不做,但也许我错了..?值得仔细检查,我猜。谢谢你给我带来了一个想法,至少。 :)
    • 我注意到您提到他们共享 FD,但您从未提及您是如何实现这一点的,因此只要我们没有看到代码,这可能是一个潜在的错误假设。 ;)
    • 同意。我可能应该在问题中提到 fd 表是如何共享的,但不想让它比现在更长......
    猜你喜欢
    • 2018-07-01
    • 1970-01-01
    • 2013-10-03
    • 2016-09-01
    • 2011-12-10
    • 1970-01-01
    • 2013-09-29
    • 1970-01-01
    • 2014-02-07
    相关资源
    最近更新 更多