select poll epoll

目标:都是为了同时检查多个文件描述符,看是否准备好了执行I/O操作

水平触发与边缘触发:

水平触发通知:如果文件描述符上可以执行I/O系统调用,则认为已经就绪。例如:输入缓存收到50字节数据,将会注册该事件,但若只读走20字节还剩下30字节,那么之后依然会注册事件。也就是说,只要还有数据就会注册。

边缘触发通知:如果文件描述符自上次状态检查以来有了新的I/O活动,此时需要触发通知。如:仅在输入缓存收到数据时注册一次,此后即使还有数据也不会再注册,直到有了新的I/O执行。

 

select和poll只能水平触发,而epoll两种模式都可以。此外在select,poll,epoll三种IO模型中常与非阻塞I/O搭配,原因是:

如果多个进程或者线程都在同一个打开的文件描述符上进行I/O操作,那么在通知就绪和后续执行I/O调用之间的这段时间,文件描述符的状态就可能改变,如果此时是阻塞式的I/O将会造成阻塞。

 

select:

int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout);

返回值是正整数表示通知就绪的文件描述符个数,此时返回的文件描述符集合都要进行检查来找出发生I/O事件是什么,0是timeout已经超时了,-1表示错误发生。其中,fd_set是文件描述符集合的数据类型,readfds指向的是检测输入是否就绪的文件描述符集合,writefds 是检测输出是否就绪的文件描述符集合,exceptfds是用来检测异常情况是否发生的文件描述符集合。nfds是比三个集合中最大的文件描述符还要大1,这样内核就不用去检查大于这个值的文件描述符号是否属于这些文件描述符集合。

 

timeout控制着select的阻塞行为,指向一个timeval结构体。

timeout=NULL,select() 会一直阻塞直到:

三个文件描述符集合中有就绪的。

该调用被信号处理例程中断

timeout指定的时间上限已到

struct timeval

{

time_t  tv_sec;

suseconds_t tv_usec;

};

若timeval的两个成员值都为0,select()只是简单地轮询指定的文件描述符集合,而且是立刻返回。否则timeout将是select()指定的一个等待时间上限。

关于文件描述符集合fd_set的操作由四个宏完成。

FD_ZERO(fd_set* fdset);将集合初始化为空

FD_SET(int fd,fd_set* fdset);将文件描述符fd添加到集合中

FD_CLR(int fd,fd_set* fdset);将文件描述符fd从fd_set指向到集合中移出

FD_ISSET(int fd,fd_set* fdset);检查fd是否是fd_set的成员,是则返回true。

其中文件描述符集合有一个最大容量限制,由常量FD_SETSIZE来决定,在linux上值为1024。

 

在调用select()之前,readfds,writefds,exceptfds所指向的集合必须初始化,若对某一类事件不感兴趣那么可以把对应的参数指定为NULL。当select()返回时,它们指向集合包含的就是已经处于就绪状态的文件描述符集合了,用FD_ISSET()来进行检查。同时,因为这些结构体会在调用的时候被修改,所以在循环使用select时,必须要每次都重新初始化。

 

poll:

poll和select之间的主要区别在于如何指定待检查的文件描述符

select 中是提供三个事件集合,在每个集合中标明感兴趣的文件描述符。

poll是反过来,提供一列文件描述符,在每个文件描述符上表明我们感兴趣的事件。

 

int poll(struct pollfd fds[],nfds_t nfds,int timeout)

nfds指定数组fds中元素的个数,nfds_t 实际为unsigned int

若timeout==-1 poll()会阻塞,其余值和select情况相同

返回值为正整数表明就绪的文件描述符,0表明到达timeout时间上限,-1表示错误。

fds 是我们需要poll()来检查的文件描述符数组,pollfd结构体是

struct pollfd
{

int fd;

short events;

short revents;

};

events 和 revents字段都是位掩码。调用者初始化events指定该文件描述符要检查的事件,revents在poll()返回后表示该文件描述符上实际发生的事件。如果对某个特定的文件描述符上的事件不感兴趣,可将events设为0,那么revents总会返回0。

与输入事件有关的:POLLIN   POLLRDNORM  POLLRDBAND   POLLPRI  POLLRDHUP

与输出事件有关的:POLLOUT  POLLWRNORM   POLLWRBAND

附加信息: POLLERR  POLLHUP  POLLNVAL

 

 

select 和 poll 之间的一些区别

select 使用的fd_set 有一个上限限制,poll没有

select 的fd_set即是参数也是要保存结果的地方,重复循环调用需要重复初始化,而poll通过events和revents来处理,避免了麻烦的初始化

select的超时精度比poll的高

poll通过POLLNVAL会响应文件描述符关闭事件,而select只会返回-1,设置错误码为EBADF

 

在性能方面

当待检查的文件描述符范围较小,分布密集时,select和poll性能表现相似

当分布稀疏,poll明显比select好。

 

存在的问题:

(1) 每次调用select和poll时,内核都要重新检查所有被指定的文件描述符,该操作很耗时

(2)调用select和poll,程序都必须传递一个需要被检查的文件描述符的数据结构到内核,内核检查完了修改数据结构返回给程序,对于poll,数据结构大小随着检查文件描述符数量增加而增加,对于select 数据结构大小固定为FD_SETSIZE。这个从用户空间到内核空间来回拷贝数据结构到过程很耗CPU时间

(3)select或poll调用完成后,行必须检查返回的数据结构中的每个元素,以此检查哪个就绪了。

 

epoll:

主要优点是:

当检查大量的文件描述符时,epoll的性能比select和poll好很多。

epoll同时支持水平触发和边缘触发。

灵活性高。

epoll实例和一个打开的文件描述符相关,这个文件描述符是内核数据结构的句柄。这些内核数据结构实现了两个目的:

兴趣列表:记录需要检查的文件描述符列表;

就绪列表:维护处于就绪状态的文件描述符列表;

其中,就绪列表是兴趣列表的子集

epoll_create() 创建了一个新的epoll实例,对应的兴趣列表初始化为空。

int epoll_create(int size)

返回-1为错误,正整数为epoll实例的文件描述符。当所有与epoll实例相关的文件描述符被关闭时,实例销毁。

size指定一个希望检查文件描述符的个数,但这只是为内部数据结构划分初始大小而不是上限

 

int epoll_ctl(int epfd,int op,int fd,struct epoll_event* ev);

struct epoll_event

{

uint32_t events;

epoll_data_t data;

};

events字段是一个位掩码,指定了待检查的描述符fd上所感兴趣的事件集合。

data字段的成员在fd成为就绪态时可以用来指定传回的信息。

typedef union epoll_data

{

void* ptr;

int fd;

uint32_t u32;

uint64_t u64;

}epoll_data_t;

修改由epfd所代表的epoll实例中的兴趣列表。fd指明具体修改的是哪一个文件描述符(fd不能是普通文件或者目录的文件描述符)。需要注意的是,当把文件描述符添加到兴趣列表时,要么把ev.data.fd设置为文件描述符号,要么把ev.data.ptr设为指向包含文件描述符号的结构体。

op用来指定需要进行的操作:

EPOLL_CTL_ADD将fd加到兴趣列表中,对于fd上我们感兴趣的事件,都在ev指向的结构体中。如果试图添加一个已经存在的文件描述符就会出现EEXIST错误。

EPOLL_CTL_MOD修改fd上设定的事件,需要ev指向结构体上的信息。如果该文件描述符不在兴趣列表中,出现ENOENT错误。

EPOLL_CTL_DEL将文件描述符从兴趣列表移出,关闭一个文件描述符会自动将其从所有epoll实例的兴趣列表中移除。

 

max_user_watches 上限

因为每个注册到epoll实例上的文件描述符都需要占用一小段不能被交换到内核内存空间,因此内核提供了一个接口定义每个用户可以注册到epoll实例上文件描述符总数。

 

int epoll_wait(int epfd,struct epoll_event* evlist,int maxevents,int timeout)返回epoll实例中处于就绪态的文件描述符信息。timeout==-1 一直阻塞,timeout==0执行一次非阻塞的检查。

数组evlist中,每个元素返回的都是某一个对应的就绪文件描述符的信息,events字段返回了在该描述符上已经发生的事件掩码

 

(1)当我们使用dup()复制一个epoll文件描述符后,被复制的描述符所指代的epoll兴趣列表和就绪列表与原始的epoll文件描述符相同。当需要修改兴趣列表时,在epoll_ctl()上的epfd可以是原始的也可以是复制的。

  (2)fork()调用后的情况也是如此,子进程复制了epoll文件描述符,指向相同的epoll数据结构。

 

默认情况下epoll提供的是水平触发通知。若要使用边缘触发通知:

struct epoll_event ev;

ev.data.fd = fd

ev.events = EPOLLET;

epoll_clt(epfd,EPOLL_CTL_ADD,fd,ev);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值