epoll为什么高效

##无需遍历整个fd集合
首先回忆一下select 模型,当有I/O 事件到来时,select 通知应用程序有事件到了快去处理,而应用程序必须轮询所有的fd集合,测试每个fd是否有事件发生,并处理事件;代码像下面这样:

int res = select(maxfd+1, &readfds,NULL, NULL, 120);
if (res > 0)
{
    for (int i = 0; i <MAX_CONNECTION; i++)
    {
       if (FD_ISSET(allConnection[i], &readfds))
       {
           handleEvent(allConnection[i]);
       }
    }
}
// if(res == 0) handle timeout, res < 0handle error

Epoll 不仅会告诉应用程序有I/0事件到来,还会告诉应用程序相关的信息,这些信息是应用程序填充的,因此根据这些信息应用程序就能直接定位到事件,而不必遍历整个fd集合。

int res = epoll_wait(epfd, events, 20,120);
for (int i = 0; i < res;i++)
{
    handleEvent(events[n]);
}

##Epoll 关键数据结构
Epoll 速度快和其数据结构密不可分,其关键数据结构就是:

struct epoll_event {
    __uint32_tevents;      // Epoll events
    epoll_data_tdata;      // User data variable
};
typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

可见epoll_data 是一个 union 结构体 , 借助于它应用程序可以保存很多类型的信息 :fd 、指针等等。有了它,应用程序就可以直接定位目标了。

别小看了这些效率的提高,在一个大规模并发的服务器中,轮询IO是最耗时间的操作之一。

对比最早给出的阻塞IO的处理模型, 可以看到采用了多路复用IO之后, 程序可以自由的进行自己除了IO操作之外的工作, 只有到IO状态发生变化的时候由多路复用IO进行通知, 然后再采取相应的操作, 而不用一直阻塞等待IO状态发生变化了.