##无需遍历整个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状态发生变化了.