先简单介绍一下I/O复用,I/O复用使得程序可以同时监听多个文件描述符,主要通过select、poll和epoll三个函数来实现;
(1)select系统调用
select系统调用的用途是:在一段指定的时间内。监听用户感兴趣的文件描述符上的可读、可写、和异常事件处理。
select事件原型如下:
#include<sys/select.h>
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set* exceptfds; struct timeval *timeout);
1)nfds参数指定被监听的文件描述符的总数;通常设置为select监听的所有文件描述符中的最大值加1,因为文件描述符是从0开始计数的;
2)readfds、writefds、exceptfds三个参数分别指可读、可写、异常对应的文件描述符集合;
3)timeout用来设置select函数的超时时间;
(2)poll系统调用
poll系统调用和select类似,指在一定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者;
poll函数原型如下:
#include<poll.h>
int poll(struct pollfd* fds, nfds_t nfds, int timeout)
// fds 是一个pollfd结构类型的数组;nfds指被监听事件集合fds的大小;timeout指poll的超时值
struct pollfd{
int fd; //文件描述符
short events; //注册的时间
short revents;//实际发生的时间,由内核填充
}
(3)epoll系列系统调用
epoll是Linux特有的I/O复用函数。在实现和使用上与select、poll有很大的差别。首先使用一组函数来完成任务,而不是单个函数,其次,epoll把用户关心的文件描述符上的时间放在内核里的一个事件表中,从而无须像select和poll那样每次调用都需要重读传入文件描述符集或者事件集。epoll需要使用一个额外的文件描述符来唯一标识内核中的时间表。文件描述符(用来标识内核中的事件表)的创建有epoll_creat来完成:
#include<sys/epoll.h>
int epoll_creat(int size)
操作epoll的内核时间表:
#include<sys/epoll.h>
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event)
fd参数是要操作的文件描述符,op参数则指定操作类型(三种)
(1)EPOLL_CTL_ADD(往事件表中注册fd上大的事件)
(2)EPOLL_CTL_MOD(修改fd上的注册事件)
(3)EPOLL_CTL_DEL(删除fd上的注册事件)
event参数指定事件,它是epoll_event结构指针类型
epoll_wait函数:
epoll系列系统调用的主要接口是epoll_wait函数,它在一段超时时间内等待一组文件描述符上的事件
#include<sys/epoll.h>
int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout)
该函数成功时返回就绪的文件描述符的个数,失败时返回-1并设置errno
maxevent参数指定最多监听多少个事件,它必须大于0
LT和ET模式
epoll对文件描述符的操作有两种模式:LT(电平触发)模式(默认工作模式)和ET(边沿触发)模式(高效工作模式)
LT:当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件,当应用程序下一次调用epoll_wait时,epoll_wait还会再次向应用程序通告此事件,直到该事件被处理。
ET:当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,因为后续的epoll_wait调用将不再向应用程序通知这一事件。
Select,poll和epoll的区别: