一. 关于epoll
对于IO复用模型,前面谈论过了关于select和poll函数的使用,select提供给用户一个关于存储事件的数据结构fd_set来统一监测等待事件的就绪,分为读、写和异常事件集;而poll则是用一个个的pollfd类型的结构体管理事件的文件描述符和事件所关心的events,并通过结构体里面的输出型参数revents来通知用户事件的就绪状态;
但是对于上述两种函数,都是需要用户遍历所有的事件集合来确定到底是哪一个或者是哪些事件已经就绪可以进行数据的处理了,因此当要处理等待的事件比较多时,就会有数据复制和系统遍历的开销导致效率并不高效;针对select和poll的缺点,另外一种相对高效的处理IO复用的函数就出现了,那就是epoll;
二. epoll相关函数的使用
首先,和select及poll函数不同的是,epoll并没有直接的一个用epoll来命名的函数使用,而是分别提供出来三个函数:epoll_create、epoll_ctl和epoll_wait;
epoll_create
epoll_create函数创建一个epoll的“实例”,请求内核分配一个指定大小的空间用于事件的后台存储,函数参数size只是一个关于内核如何维护内部结构的提示,不过现在这个size已经被忽略并不需要在意了;
函数成功会返回一个引用新创建的epoll实例的一个文件描述符,用于随后调用其他的epoll函数的结构,如果不再需要的话,应当使用close函数关闭,这时内核会销毁该epoll实例并释放相关资源;如果函数失败会返回-1并置相应的错误码;
2. epoll_ctl
函数参数中,
epfd是用epoll_create创建出来的epoll文件描述符,用来操纵epoll实例;
op是要对创建出的epoll实例进行操作,而op的操作选项有如下三种宏:
EPOLL_CTL_ADD用于在epfd标识的epoll实例中添加登记要处理的事件;
EPOLL_CTL_MOD用于更改特定的文件描述符所关心的事件;
EPOLL_CTL_DEL用于删除在epoll实例中登记的事件,标识并不需要再关心了;
fd是指要进行数据IO的事件的文件描述符,也就是用户需要进行操作的事件的文件描述符;
event是一个epoll_event的结构体,用于存放需要对fd进行操作的相关信息:
结构体中,
events表示文件描述符fd所对应的事件所关心的操作,是相应的比特位的设置,有如下几种宏:
如上的宏中,最主要使用的有如下几种:
EPOLLIN表示fd可以进行数据的读取;
EPOLLOUT表示fd可以进行数据的写入;
EPOLLPRI表示当前有紧急数据可供读取;
EPOLLERR表示当前事件发生错误;
EPOLLHUP表示当前事件被挂断;
EPOLLET将相关的文件描述符设置为边缘触发,因为默认是水平触发的;对于LT和ET模式下面会讨论;
对于结构体中的data则是一个联合,用于表示有关文件描述符操作的数据信息:
ptr是指向数据缓冲区的一个指针;
fd是相应操作的文件描述符;
epoll_ctl函数成功返回0,失败返回-1并置相应的错误码;
3. epoll_wait
如果说上面的epoll_create和epoll_ctl是为了进行相关事件的操作而进行的准备工作,那么真正和select及poll函数一样用来进行多个事件的等待就绪则就是epoll_wait函数了:
函数参数中,
epfd是用epoll_create创建出的epoll实例的文件描述符;
events是上述的一个结构体的指针,这里一般是一个数组的首地址,是一个输入输出型参数,当作为输入时,是用户提供给系统一个用来存放就绪事件的地址空间,而作为输出型参数时,系统会将就绪的事件放入其中供用户提取,因此不可以为NULL;
maxevents是events的大小;
timeout则是设置等待的超时时间,单位为毫秒;
这里值得一提的是,既然epoll是select和poll的改进,那么其最主要的高效就是体现在epoll_wait的返回值:
函数失败返回-1并置相应的