1

我正在为 Linux 开发一个kevent/kqueue 仿真库。我是这个项目的新维护者,不幸的是,以前的维护者不再参与太多(所以我不能动脑筋)。

在 FreeBSD 和 macOS 下,当您close()提供的文件描述符kqeueue()释放任何与其关联的资源和事件时。

现有代码似乎没有提供类似的接口。在我向 API 添加一个函数(或恢复一个旧函数)以显式释放 kqueue 资源之前,我想知道是否有任何方法可以将触发器与 linux 中的文件描述符相关联,这样当它关闭时,我们可以清理与FD。

文件描述符本身可以是任何类型,即由 eventfd 提供的类型,或 epoll 或任何其他创建文件描述符的类型。

4

2 回答 2

1

当 pipe() 调用的最后一个写入文件描述符关闭时,epoll()/poll() 等待者将在任何仍然打开的读取文件描述符上看到 [E]POLLHUP 事件。任何表示连接而不是状态的 fd 大概也是如此。

于 2018-02-13T03:14:46.097 回答
0

对此的解决方案相当简单,如果实施起来有点烦人。它依赖于一个fcntl被调用的F_SETSIG, 来指定用于传达 FD 状态更改的信号,以及一个fcntl被调用F_SETOWN_EX来指定应该将信号传递到哪个线程。

当应用程序启动时,它会产生一个单独的监控线程。该线程用于接收 FD 生成的信号。

在我们的特定用例中,监视线程必须在第一次创建受监视的 FD 时隐式启动,并在没有显式连接的情况下销毁。这是因为我们正在模拟一个 FreeBSD API (kqueue),它没有显式的 init 和 deinit 函数。

监控线程:

  1. 监听我们传递给的信号F_SETSIG
  2. 获取其线程 ID并将其存储在全局中。
  3. 使用 .通知应用程序监视线程已启动(并且全局已填充)pthread_cond_broadcast
  4. 调用pthread_detach以确保正确清理它,而无需另一个线程需要执行显式pthread_join.
  5. 呼叫sigwaitinfo等待信号的传递。

应用程序线程:

  1. 用于pthread_once在第一次创建 FD 时启动监控线程,然后等待监控线程完全启动。
  2. 用于F_SETSIG指定 FD 打开/关闭时发送的信号,并将F_SETOWN_EX这些信号定向到监控线程。

当被监控的 FD 关闭sigwaitinfo时,监控线程中的调用返回。在我们的例子中,我们使用管道来表示 kqueue,因此我们需要将我们接收到的信号的 FD映射到与我们需要释放的资源(kqueues)相关联的那个。一旦这个映射完成,我们可以(更多信息见下文)清理与 FD 对关联的资源,并sigwaitinfo再次调用以等待更多信号。

该策略的其他关键部分之一是与 FD 关联的资源是引用计数的。这是因为信号不是同步传递的,所以可以关闭一个 FD,并且可以在指示原始 FD 已关闭、传递和操作的信号之前创建一个具有相同编号的新 FD。这显然会导致活动资源被释放的大问题。

为了解决这个问题,我们维护了一个互斥同步 FD 到资源映射数组。此数组中的每个元素都包含特定 FD 的引用计数。

如果在创建新管道/资源对时重用 FD 之前未传递信号,则该特定 FD 的引用计数将 > 0。发生这种情况时,我们立即释放资源,并重新初始化它,增加引用数数。当指示 FD 已关闭的信号被传递时,引用计数递减(但不为零),并且不释放资源。

或者,如果在重用 FD 之前传递了信号,那么监控线程会将引用计数减为零,并立即释放相关资源。

如果此描述有点令人困惑,您可以使用上面的任何链接查看我们在现实世界中的实现。

注意:我们的实现与上面描述的不完全一样(值得注意的是,我们在创建新的 FD/资源映射时不检查 FD 的引用计数)。我认为这是因为我们依赖于这样一个事实,即关闭一个管道并不一定会导致另一端被关闭,因此开放端的 FD 不能立即用于重用。不幸的是,编写代码的开发人员无法查询。

于 2019-09-15T18:19:12.543 回答