【问题标题】:How to safely delete a socket file descriptor used with epoll in a multithreaded program?如何在多线程程序中安全地删除与 epoll 一起使用的套接字文件描述符?
【发布时间】:2021-06-23 09:25:31
【问题描述】:

我正在尝试解决 epoll 事件循环正在运行的 epoll 竞争条件问题,我希望它停止监视套接字文件描述符 (FD),但在调用 epoll_ctl(..., EPOLL_CTL_DEL, ...) 后我真的不知道 epoll 是否instance 实际删除了它,或者它正在处理与 FD 关联的事件,或者它只是从 epoll_wait() 唤醒线程。

我google了一下,发现一个特殊的内核补丁添加了EPOLL_CTL_DISABLE操作(here)。遗憾的是,它需要EPOLLONESHOT,我认为这不是很有效,因为重新配置不是一个简单的操作(它需要在 epoll 的红黑树中进行搜索)。

因此,我想知道我自己的解决方案。我显然可以停止事件循环的线程并最终在处理完我的套接字文件描述符后重新创建它,我可以通过简单地执行pthread_join() 知道线程何时消失,但我们不要天真,@987654327 @ 会使内核释放线程的资源,然后pthread_create() 将再次分配相同的资源,这并不像EPOLLONESHOT 的情况那样理想,并且肯定会带来一些性能损失。

现在我想知道其他事情。如果我能以某种方式在epoll_wait() 将唤醒的套接字上手动调度一个事件,我可以在它唤醒后部署任何阻塞同步方法以确保它不会继续下去。这样,它将进入下一个epoll_wait() 迭代,在那里它将意识到套接字已被删除并且不会发生竞争条件。举例来说,如下所示(实际上可以使用任何东西而不是障碍):

/* Obviously, never call the function from
within the event loop, or a deadlock will emerge */
void safe_socket_delete(int sfd) {
  atomic_store(&epoll->should_wait, 1);
  /* This is the function I'm looking for.
  I need to use it to be sure that the event
  loop will go through the should_wait if */
  dispatch_event(sfd, some_event);
  pthread_barrier_wait(&epoll->barrier);
  // we do whatever we want with the socket now
  pthread_barrier_wait(&epoll->barrier);
}

void event_loop() {
  while(1) {
    int events = epoll_wait(epoll->fd, ...);
    if(atomic_load(&epoll->should_wait) == 1) {
      pthread_barrier_wait(&epoll->barrier);
      pthread_barrier_wait(&epoll->barrier);
      continue;
    }
    // handle the events
  }
}

有这样的功能吗?如果没有,我该如何有效地处理这个问题?

也许我可以以不同的方式处理这个问题。与其在现有套接字上调度事件以唤醒epoll_wait(),也许我可以使用eventfd() 创建一个新事件FD,然后使其可读或可写,在需要时将其添加到epoll,然后tada。事后将其删除。我会多想一点,也许试试这个想法。当然,它带有 2 次树搜索,但在需要时进行 2 次搜索比在不需要时进行搜索更好(总是需要用 EPOLLONESHOT 重新武装)。

巨大的优化是始终拥有一个带有EPOLLET 的此类事件文件描述符,以便我可以在其上模拟事件并让epoll_wait() 唤醒。优点:没有树搜索,缺点:每个 epoll 实例多一个文件描述符。

【问题讨论】:

    标签: c linux epoll


    【解决方案1】:

    有几种方法可以调度事件。既然您使用的是 Linux,为什么不使用 eventfd https://man7.org/linux/man-pages/man2/eventfd.2.html。创建eventfd 并将其注册到epoll。当你写入它时,你会唤醒 epoll,你可以像处理任何其他需要处理的文件描述符一样处理它。

    【讨论】:

    • 是的,这似乎是个好主意。我正在修改我的代码以立即执行此操作,如果可行,我将接受此答案。这也是一个超级便宜的解决方案。
    猜你喜欢
    • 2013-10-03
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    • 2020-12-03
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多