c - epoll, kqueue, 用户指定指针 : how to safely deallocate it in a multithreaded envinronment?

标签 c linux multithreading epoll kqueue

我们在Unices系统中可以使用的异步I/O告警的工具,比如Linux上的epoll,BSD系统上的kqueue和Solaris的/dev/poll或者I/O Ports,都是让用户指定一个指针来进行与用户希望接收 I/O 警报的文件描述符相关联。

通常在这个指针中,用户指定指向一个结构的指针,该结构将抽象出一个文件描述符(例如“Stream”结构,或类似的东西),并且用户将在每次新文件描述符时分配一个新结构是开放的。

例如结构流 { int fd;内部标志; callback_t on_read_fn;/* ... */};

现在,我的问题是:如何安全地释放用户在多线程环境中分配的这个结构?

我问这个,因为 epoll/kqueue/etc 的性质: 您通常有一个线程从内核“下载”一个事件 vector ,其中包含具有一些 I/O 就绪状态的文件描述符,以及与该文件描述符关联的用户指针。

现在,让我们考虑我有 2 个线程:T1,它下载这些事件并处理它们,例如调用 stream->on_read_fn(); 等,T2 只运行用户代码、用户事件和类似的东西。

如果 T2 想要关闭一个文件描述符,只需执行 close(stream->fd); 并且 T1 将不再收到该 fd 的任何 I/O 警报,因此可以安全地释放stream 结构。

但是,如果 T1 线程已经在它正在处理的事件 vector 中下载了完全相同的文件描述符,但它还没有处理该文件描述符呢?

如果 T1 在 T2 之前被调度,它会正常,但是如果 T2 在 T1 之前被调度,它会关闭文件描述符并释放 stream 结构,所以线程 T1,当它会处理那个文件描述符,将有一个用户关联的指针,指向一个已经释放的结构!当然这会严重崩溃。

我的观点是,T2 永远 不会知道线程 T1 是否为该特定文件描述符下载了一些 I/O 警报,T2 也无法预测 T1 是否会下载一些 I/O 警报完全没有!

这非常棘手,让我头晕目眩。有什么想法吗? 在这种情况下,何时释放用户指定的指针是安全的?

注意:我的一个 friend 建议在调用 close(2) 之前从 epoll/kqueue 队列中删除文件描述符。没错,这就是我现在所做的,但这并不能解决问题,因为 T2 可以从 epoll/kqueue 队列中删除文件描述符,但这不能确保该文件的 I/O 事件描述符尚未从内核“下载”,将很快由线程 T1 处理。

最佳答案

我遇到了完全相似的问题,这就是为什么在新的 linux 内核提案中,有人(不记得名字了)建议为 FD 实现 DISABLED 状态,这样如果它已被另一个线程释放,您就可以跳过处理。

就个人而言,我从多线程 epool 调用转移到 FD 上的单线程 epool() ,然后将事件调度到多个线程。内部的对象本身是引用计数的,稍后由垃圾收集器收集。对于多线程 epool 解决方案,诚实地工作得很好并且没有明显的退化......

* 已编辑 *

此外,我研究了另一种方法,通过创建一个受互斥锁保护的 std::set 并在需要关闭 FD 时由消费者线程填充,从而从与处理 epool 相同的线程关闭 FD。这也很有效。

关于c - epoll, kqueue, 用户指定指针 : how to safely deallocate it in a multithreaded envinronment?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28764101/

相关文章:

c++ - 使用 C++ 从 MJPEG 流中捕获视频

c - C 中的 forks - 练习

c++ - 我在从文件读取的 getc () 中有一个段错误

linux - lxde 意外退出

c++ - undefined reference 错误,即使该类已链接

linux 标准输入、标准输出管道

C#通过事件同步两个对象

c - 在另一个函数中使用输入的值

C#/Java lock(object) 的 c++ 模拟

c++ - STL vector 多线程