异步IO框架实现之EPOLL
http://blog.csdn.net/dreamfreelancer/archive/2009/07/28/4387375.aspx
Linux2.5.44之后,主流的异步IO机制是EPOLL--尽管基于实时信号
(http://blog.csdn.net/DreamFreeLancer/archive/2009/07/27/4383193.aspx)的机制仍被支持。EPOLL是一种性能卓越且编程简单的异步IO机制,成为主流也算是众望所归。本项目的异步IO框架
(http://blog.csdn.net/DreamFreeLancer/archive/2009/07/26/4381316.aspx)提供了基于EPOLL的实现。
实现要点介绍如下:
应用EPOLL机制,首先得调用函数:
int epoll_h=epoll_create(possible_max_fd_count);//创建一个文件描述符,epoll_h: 对应EPOLL自身--这一点和Windows下的完成端口(Completion Port)很象,后者也需要创建一个Handle,对应
完成端口本身。注意,epoll_create中的possible_max_fd_count参数并不是指最大文件描述符数量,它仅仅是可能会用 到的文件描述符(如Socket)最大数量的一个估计值,并不要求很准确,因为它的作用只是供操作系统预分配内部数据结构用,不宜估得过大,偏小问题不 大。
实现bool aio_provider_t::register_fd(aio_sap_it *dest_sap, int32 fd, sp_aioeh_t& eh):
::fcntl(fd, F_SETFL, O_NONBLOCK);//将Socket设成非阻塞
struct epoll_event ev;
ev.data.fd=fd;
//指定需要监测的事件类型,和事件触发方式(下面详细解释)
ev.events=EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLET;
EPOLL有两种事件触发方式,即所谓“边沿触发(Edge Triggered)”(EPOLLET)和“水平触发(Level
Triggered)”(缺省),边沿触发,效率较高,只在Socket发送缓冲区由满变成不满和接收缓冲区由空变
成非空的瞬间,EPOLL会分别检测到EPOLLOUT和EPOLLIN事件,其它时候,没有任何事件可被检测到,为确保Socket上的收、发正常,应用程序必需确保“发则发到发不出,收必收至收不到”--前文讨论过的实时
信号机制即相当于这里的边沿触发方式,因此,也需遵从相同的原则。
至于水平触发方式,只要发送缓冲区不为满,即可检测到EPOLLOUT,只要接收缓冲区不为空,即可检测到EPOLLIN,效率不如前者高,但编程更容易,不容易出错。
::epoll_ctl(epoll_h, EPOLL_CTL_ADD, fd, &ev); //将Socket绑定到EPOLL描述符上
struct epoll_event evs[EPOLL_WAIT_SIZE]={0}; //EPOLL_WAIT_SIZE指定一次等待最多可返回的Socket数,如果同时有事件发生的Socket大于该值,将在下次检查时返回
int ep_cnt=epoll_wait(epoll_h, evs, EPOLL_WAIT_SIZE, ms_timeout);//在EPOLL描述符上等待所有已注册Socket上的异步IO事件,返回有事件Socket数量(最大不超过 EPOLL_WAIT_SIZE,超出部分需等到下一次epoll_wait返回),若无任何事件则Block线程至ms_timeout。另外一个值得 注意的事实是:epoll_wait一次等待可能在同一Socket上检测到多个事件,应用程序需通过按位与分别检出每个事件,然后就是沿着异步IO框架 把事件投递给应用程序。
好了,EPOLL就这么简单,epoll_create, epoll_ctl和epoll_wait,三板斧搞定令很多初学者望而却步的
可伸缩(Scalable)异步IO编程问题。
另外如前文所述,EPOLL和实时信号相同,都属于“可用通知型(Availability Notification)”异步IO机制,和完成端口(Completion Port)有较大区别,至于后者,后续将专门论述