select
select机制问题
1、每次调用,都需要把fd_set
集合从用户态拷贝到内核态,如果fd_set
集合过大,开销也会很大
2、同时每次调用select都需要在内核遍历传过来的fd_set
,同事遍历时间复杂度为O(n)
3、所以为了性能,内核对fd_set
做了限制,大小不可变(32位机默认是1024个,64为2048)
int select(
int maxfdp1, //int maxfdp1 指定待测试的文件描述字个数,它的值是待测试的最大描述字加1。
//fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄
fd_set *readset,
fd_set *writeset,
fd_set *exceptset,
const struct timeval *timeout);
【返回值】
int 若有就绪描述符返回其数目,若超时则为0,若出错则为-1
select()通过fd_set
的数据结构,实际上是一个long类型的数组,每一个数组元素都能与一打开的文件句柄建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读。
每次调用select,都需要把fd_set
集合从用户态copy到内核态(还有内核态->用户态),如果集合很大,开销也大
poll
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
typedef struct pollfd {
int fd; // 需要被检测或选择的文件描述符
short events; // 对文件描述符fd上感兴趣的事件
short revents; // 文件描述符fd上当前实际发生的事件
} pollfd_t;
与select不同的是,采用链表pollfd
替换原来的fd_set
,没有连接数限制,基本与select一致
epoll
//创建一个epoll句柄,参数size表明内核要监听的描述符数量。
//调用成功时返回一个epoll句柄描述符,失败时返回-1。
int epoll_create(int size);
int epoll_ctl(
//epfd 表示epoll句柄
int epfd,
//表示fd操作类型
//EPOLL_CTL_ADD 注册新的fd到epfd中
//EPOLL_CTL_MOD 修改已注册的fd的监听事件
//EPOLL_CTL_DEL 从epfd中删除一个fd
int op,
//描述符
int fd,
//监听的事件
struct epoll_event *event);
int epoll_wait(
int epfd,
//从内核得到的就绪事件集合
struct epoll_event * events,
//内核events的大小
int maxevents,
//等待的超时事件
int timeout);
epoll两种工作方式
水平触发(LT):默认工作模式,即当epoll_wait检测到某描述符事件就绪并通知应用程序时,应用程序可以不立即处理该事件;下次调用epoll_wait时,会再次通知此事件
边缘触发(ET): 当epoll_wait检测到某描述符事件就绪并通知应用程序时,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次通知此事件。(直到你做了某些操作导致该描述符变成未就绪状态了,也就是说边缘触发只在状态由未就绪变为就绪时只通知一次)
由此可见:ET模式比LT模式的效率要高,如果使用ET模式,要保证每次进行数据处理的时候,要将其处理完,不要造成数据丢失,ET模式只支持非阻塞的读写,为了保证数据完整性
总结: