首页 > 代码库 > epoll
epoll
epoll的红黑树由一个互斥量保护,ready list是自旋锁保护的。
ready list涉及到add\mod\wait
红黑树涉及到add\mod\del
每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。
而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。
EPOLLIN
ET模式:
每次EPOLL_CTL_ADD或EPOLL_CTL_MOD时,如果加入前,就是可读状态,那么加入后会触发1次 不管sock缓冲是否读完,只要对方有send或connect或cloase或强退,就会触发EPOLLIN
LT模式:只要socket可读,就会一直触发EPOLLIN
EPOLLOUT
ET模式:
每次EPOLL_CTL_ADD或EPOLL_CTL_MOD时,如果加入前,就是可写状态,那么加入后会触发1次 如果EPOLLOUT与EPOLLIN一起注册,不管sock发送缓冲是否从满变不满,只要socket发送是不满的,那么每次EPOLLIN触发时,都会触发EPOLLOUT
LT模式:只要socket可写,就会一直触发EPOLLOUT
简单来说,ET模式下,只要监听了EPOLLIN和EPOLLOUT,socket的每次动作(包括close与不close直接强退),都会触发1次, 与读写缓冲的状态无关。
EPOLLHUP
每次有未连接的socket被加入监听列队时,会触发EPOLLHUP
比如socket创建了套接字以后未调用connect或未调用listen,就直接加入epoll监听列队
或者close了以后再加入epoll监听列队
在多线程环境中使用epoll的时候,都会有下面的疑问: 1.能否在多个线程中对同一个epoll描述符调用epoll_wait。
通过以上的介绍,可以看到,这么做是可以的,但是带来的好处其实并不大。其实在epoll_wait所做的处理相当少,在epoll的实现中,epoll_wait其实就是担当一个epoll的就绪队列的消费者的角色,它的真正作用就是提取就绪队列中的IO事件的信息,如果就绪队列为空,epoll_wait就进入等待,一直等到文件对象通知新的IO事件并将它唤醒。
而且,另外一点需要注意的是,如果在注册世间的时候不指定EPOLLONESHOT或者EPOLLET标志,对同一个文件描述符,多个线程可能会触发多个事件.
2.当一个线程正在调用epoll_wait的时候,另一个线程使用epoll_ctrl在epoll中加入了一个新的socket描述符,那么对该socket的描述符的IO事件的监控是否立刻就开始,还是说需要等到下一次进入epoll_wait调用的时候才生效?
通过以上的介绍,可以知道,对新的socket描述符的监控是立即生效的。
3.当一个线程正在调用epoll_wait的时候,另一个线程使用epoll_ctrl删除一个socket描述符后,那么对该socket描述符的IO事件的监控是否立刻就结束?
这个问题其实涉及到一个很微妙的竞态条件,首先,可以肯定地是,如果在调用epoll_ctrl删除一个socket描述符后调用epoll_wait,那么epoll_wait是不会返回和该socket有关的事件的。
但是上面描述的情况存在这样的可能性:
1。线程1调用epoll_wait等待, socket #1触发一个IO事件将epoll_wait唤醒,epoll_wait将socket #1的事件拷贝到用户的epoll_event结构中。
2。线程2调用epoll_ctrl将socket #1删除
3。线程1从epoll_wait返回
在这种情况下,在步骤3还是有可能返回socket #1的事件通知,尽管我们已经在步骤2将该socket从epoll中删除了。
如果我们在步骤2的时候将socket #1相关的应用的数据结构删除了,那么在步骤3后通过epoll_event中的data.ptr字段去引用的话,就可能出现crash的问题。
如果一个线程正阻塞在epoll_pwait上,此时可能有另外一个线程要把一个socket fd添加到这个epoll fd上,如果这个这个新的socket fd被添加进去后处于ready状态,那么epoll_wait就不会再处于阻塞状态。如果由epoll fd监控的一个socket fd被另外一个线程close掉,此时系统处于何种状态请参考select(2)。通过"man 2 select"后.
如果一个线程中由select管理的socket被另外一个线程close掉,将会发生什么只有天晓得。在一些UNIX系统中,select会结束阻塞态并返回,它会标识这个socket处于ready状态(后面对这个socket的操作会失败,os也会给出错误提示,除非在select返回和进程对这个socket进行读写这段时间段内,os又把同一个socket fd分配出去了)。在linux(和其他同类的系统)上,这种行为不会影响select(即有阻塞态变为非阻塞态)。总之,如果一个程序中这种行为应该被认为是一个bug(就不应有这种行为操作)。
epoll