最近在学习IO模型,就突然想到了epoll属于哪个IO模型这个问题。要研究这个问题,我们首先要知道有哪些常用的IO模型。
常见的IO模型如下:
阻塞IO:程序阻塞直到IO可用。最简单的IO模型,你平时调用的recvfrom接收函数就属于这个模型。
非阻塞IO:程序调用IO函数后立刻返回,无论当前IO可用不可用。如果你把套接字设置成了非阻塞模式,那么你使用的就是这个模型了。
复用IO:程序阻塞到多个IO文件描述符上边,当至少一个IO可用的时候返回。select就属于是IO复用。
信号驱动IO:IO可用后程序被中断并执行相应的处理函数来读写IO。其他时间程序可以正常执行其他命令。
异步IO:异步IO和信号驱动IO的区别在于异步IO会在IO数据准备好(从内核复制到用户层)再通知程序去进行处理。
看完这些模型后我们还要知道epoll的基本机制。epoll中用户需要预先向epoll注册相关IO事件,然后调用epoll的主循环epoll_wait(),之后当IO事件发生的时候该函数返回所发生的IO事件的集合,并交由用户进行处理。
很明显,epoll不是前两种模型,现在的问题就在于epoll属于后三者的哪一个。
先看复用IO。复用IO的最主要特点在于阻塞到多个IO上边,当某个IO可用的时候返回。epoll中需要调用epoll_wait,然后epoll_wait返回可用IO处理,并交由用户处理。此时我们一般还要调用recv等函数去读写IO。这样一分析,epoll符合所有复用IO模型的特点,而且epoll本身的使用方法也像极了selec。
然后看看信号驱动IO。信号驱动IO的特点是IO可用的时候程序被中断,然后由中断函数处理IO事件。epoll中我们需要去执行主循环函数等待IO事件,并且IO事件的处理是由接下来的程序处理的,而非由某个回调函数自动处理,所以epoll明显不属于信号驱动。
最后看看异步IO。异步IO的特点在于系统是在IO数据准备好后再通知程序去处理的。而epoll只是告诉你哪个IO可用,以及这个IO的一些注册信息,真正要拿到IO数据还要靠你自己去调用recv等函数去内核拷贝。所以epoll也不属于异步IO。
综上,epoll和select一样,属于IO复用模型。 这里还要说的是,虽然epoll本身是IO复用模型,但是它的一些内部实现其实是用到了信号驱动IO、异步IO的特点,因为epoll是依靠中断来判断IO是否可用的,只不过封装到用户这里后就变成复用IO模型了。如果你想实现信号驱动IO或者复用IO,也只需要对epoll再进行简单封装。比如你可以另开一个线程去调用epoll主循环并且处理接收到的数据包,这就相当于信号驱动IO。如果再加上消息队列,每当数据包到来的时候先存放在消息队列中,之后由其他线程处理消息队列中堆积的数据包,这就相当于异步IO。
epoll所属IO模型的个人 见解