如图上图:select和poll在内核中也对应fd_set数组,可以看到这是从用户态拷贝到内核中的,而epoll的fd_set数组是内核中的数据结构,这是我们已知的二者的大不同;正因为如此,select和poll的fd_set数组,就是普通的数据,没有任何的附加功能,因此IO多路复用,硬件事件发生后(也称就绪状态),会直接赋值到这个fd_set数组中;而select和poll在每一次阻塞-唤醒,这一过程中,至少有1到n次的select的轮询工作;===》select和poll需要遍历,epoll同样也得遍历,但是epoll的机制在于遍历的内容少的吓人,epoll中内核所谓的fd_set集合,并不是遍历的对象,他其中每一个fd都对应回调函数,当就绪事件发生后,将这个真正有事件的fd连同事件,一块放到一个epollfd就绪队列中。
可以看到,每一次epoll遍历仅仅是这个fd就绪队列,这个队列中的fd全部都是就绪的,甚至可以这么说,epoll就压根没有遍历,只需判断一下fd就绪队列是否为空,不为空就返回,因此效率惊人,同比100w个fd做监视,对于那种网卡类的稀疏网络事件的情况(也就是大部分时间,甚至99%以上的时间都没事干,没流量),select和poll一般至少要遍历100w次或者200w次,甚至设置超时时间的话,在等待超时时间这段cpu就爆满了;
==》但是epoll仅仅遍历1次?2次?最坏的等待超时也仅仅是n次,数量级差太多了,这也就是epoll的优势,总结一下,也就是epoll单独搞了一个fd就绪队列的模式,减少了遍历!