在什么情况下 Linux epoll_wait return epoll_events 会构造一个空的事件字段?

Under what conditions will Linux epoll_wait return epoll_events struct with an empty events field?

我编写了一个事件循环来处理文件描述符 read/write 事件。我已经成功编写了支持kqueue的代码版本和支持select的第二版本。我正在开发我的第三个也是最后一个版本,它将支持 epoll。

我在为 EPOLLIN 事件注册新描述符时遇到问题。有问题的描述符已经是 "listening" 用于连接,所以我等待读取事件发生,以便我知道下一次调用 "accept" 会成功(非阻塞接受的常见做法)。

所有文件描述符都设置为非阻塞。

我调用了 epoll_wait returns 两个针对同一描述符的事件。第一个事件的事件字段设置为 EPOLLIN 的值。第二个事件结构的事件字段设置为 0/空。 data.fd 字段列出了与第一个结构相同的 FD 编号。

在什么情况下 epoll_wait 会 return 事件结构的事件字段为零?

这种情况并非每次都会发生,但 90% 以上的情况都会发生。

最后,我会 post 代码,但这是用 Ruby 编写的,并且有很多样板来包装 FFI 中的套接字、侦听、接受等功能,设置常量等。示例代码会很长且笨拙,所以我没有 posting 任何代码。

上面的问题是垃圾进,垃圾出的情况。在收到评论者的投诉后,我删除了 Ruby 标签,但我需要将其添加回来。问题源于 epoll_event 结构的 Ruby FFI 定义。这是原始的错误代码:

class EPollDataUnion < FFI::Union
  layout \
    :ptr, :pointer,
    :fd,  :int,
    :u32, :uint32,
    :u64, :uint64
end

class EPollEventStruct < FFI::Struct
  layout \
    :events, :uint32,
    :data, EPollDataUnion
end

上面的定义产生了一个大小为 16 字节的 EpollEventStruct。该结构应为 12 个字节。

问题是第二个结构中的 data 字段偏移了 8 个字节。默认情况下,Ruby 的 FFI 实现将所有字段对齐 8 字节边界。解决方法是指定应打包该结构。

class EPollEventStruct < FFI::Struct
  pack 1 # force alignment on 1-byte boundaries
  layout \
    :events, :uint32, # offset at byte 0
    :data, EPollDataUnion # offset at byte 4
end

因此,当我的代码将堆内存传递给 epoll_ctlepoll_wait 函数时,它对过大的事件结构进行操作。这个损坏的内存反过来产生了没有意义的损坏结果(即为同一个 FD 返回 2 个事件,第二个结构没有设置 events 位)。