基于select的I/O复用技术速度慢的原因
- 调用select函数后常见的针对所有文件描述符的循环语句
- 每次调用select函数时都需要向该函数传递监视对象信息
调用select函数后,并不是把发生变化的文件描述符单独集中到一起,而是通过观察作为监视对象的fd_set变量的变化,找出发生变化的文件描述符,因此无法避免针对所有监视对象的循环语句。而且,作为监视对象的fd_set变量会发生变化,所以调用select函数前应复制并保存原有信息,并在每次调用select函数时传递新的监视对象信息(每次调用select函数时向操作系统传递监视对象信息)。
select函数与文件描述符有关,更准确地说,是监视套接字变化的函数,而套接字是由操作系统管理的,所以select函数绝对需要借助于操作系统才能完成功能。
select的优点
select的兼容性比较高,可以支持很多的操作系统,不受平台的限制,使用select函数满足以下两个条件:
- 服务器接入者少
- 程序应该具有兼容性
epoll函数的优点
- 无需编写以监视状态变化为目的的针对所有文件描述符的循环语句
- 调用对应于select函数的epoll_wait函数时无需每次传递监视对象信息
select方式和epoll方式差异
最大差异在于监视对象文件描述符传递给操作系统的方式。select函数每次调用都要传递所有的监视对象信息,而epoll函数仅向操作系统传递1次监视对象,监视范围或内容发生变化时只通知发生变化的事项。
epoll相关函数
- epoll_create:创建保存epoll文件描述符的空间(向操作系统请求创建用于保存监视对象文件描述符的空间)
- epoll_ctl:向空间注册并销毁文件描述符(替代select方式中添加和删除监视对象文件描述符的FD_SET、FD_CLR函数)
- epoll_wait:与select函数类似,等待文件描述符发生变化(通过epoll_event结构体数组将发生变化的(发生事件的)文件描述符单独集中到一起,而无需针对所有文件描述符进行循环)
epoll_create
#include <sys/epoll.h>
int epoll_create(int size)// 成功时返回epoll文件描述符,失败时返回-1
// size传递创建文件描述符保存空间即epoll例程的大小, 仅供操作系统参考
epoll_ctl
生成epoll例程后,应在其内部注册监视对象文件描述符。
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, stuct epoll_event* event);// 成功时返回0,失败时返回-1
// epfd 用于注册监视对象的epoll例程的文件描述符
// op 用于指定监视对象的添加、删除或更改等操作
// fd 需要注册的监视对象文件描述符
// event 监视对象的事件类型
其中epoll_ctl第二个参数传递的常量及含义如下:
EPOLL_CTL_ADD:将文件描述符注册到epoll例程
EPOLL_CTL_DEL:从epoll例程中删除文件描述符
EPOLL_CTL_MOD:更改注册的文件描述符的关注事件发生情况
epoll_ctl第四个参数epoll_event的成员events中可以保存的常量及所指的事件类型如下:
EPOLLIN:需要读取数据的情况
EPOLLOUT:输出缓冲为空,可以立即发送数据的情况
EPOLLPRI:收到OOB数据的情况
EPOLLPDHUP:断开连接或半关闭的情况,这在边缘触发方式下非常有用
EPOLLERR:发生错误的情况
EPOLLET:以边缘触发的方式得到事件通知
EPOLLONESHOT:发生一次事件后,相应文件描述符不再收到事件通知。因此需要向epoll_ctl函数的第二个参数传递EPOLL_CTL_MOD,再次设置事件。
epoll_wait
#include<sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event* events, int maxevent, int timeout);// 成功时返回事件的文件描述符数,失败时返回-1
// epfd 表示事件发生监视范围的epoll例程的文件描述符
// events 保存发生事件的文件描述符集合的结构体地址值
// maxevents 第二个参数可以保存的最大事件数
// timeout 以1/1000秒为单位的等待时间,传递-1时,一直等待直到发生事件
条件触发和边缘触发
条件触发方式中,只要输入缓冲有数据就会一直通知该事件。
边缘触发方式中,输入缓冲收到数据时仅注册1此该事件,即使输入缓冲中还留有数据,也不会再进行注册。
epoll默认以条件触发方式工作,select以条件触发方式工作。如需改成边缘触发,则将epoll_ctl第四个参数epoll_event的成员events改成EPOLLIN|EPOLLET即可。
边缘触发的优点
边缘触发的优点:可以分离接收数据和处理数据的时间点,给服务端的实现带来很大灵活性。