相同点: 都是IO多路转接,都是一个线程能同一时间等待一堆描述符
不同点:
1.select接口使用不方便,每次调用完select都需要重新设置fd_set,因为输入输出未分离,返回的fd_set会将未就绪的fd清空;
epoll接口使用方便,采用事件结构方式监控,简化了对多个监控结合的操作 , 输入输出也进行了分离,不需要再重新设置fd_set;
#include<sys/select.h> //select执行过程: //在fd_set中添加要监控的fd-->调用select等待-->fd_set返回已就绪fd,未就绪的被清空 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); //nfds为最大fd_set+1,减少遍历区间 //fd_set是存储要监控fd的位图,大小是系统设定的,系统提供了一套操作fd_set的增删改查的接口 //timeout == NULL阻塞 >=0等待时间
1 //epoll执行过程: 2 //epoll_careate在内核创建eventpoll结构体,其中包含红黑树与双向链表---> 3 //为需要监控的fd创建一个事件结构并用epoll_ctl添加到红黑树中--> 4 //调用epoll_wait等待,当有fd就绪操作系统就会调用对应的回调函数将就绪的事件结构添加到双向链表中(红黑树中的就绪fd的事件类型会清空,下次遍历需要重新设置事件类型)--> 5 //epoll_wait每隔一段时间判断双向链表是否为空,如果不为空就将就绪的事件结构映射到用户传 6 //入的事件数组中 --> 7 //函数返回后,用户可以直接对返回的事件数组遍历操作 8 9 //创建eventpoll结构体 10 int epoll_create(int size); 11 12 //添加监控事件 13 int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event); 14 15 //等待就绪事件 16 int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout);
2.select每次监控都需要在用户态与内核态之间频繁拷贝数据; epoll只需要将监控的事件向内核拷贝一次,不需要频繁拷贝。
3.select监控的fd越多效率越低,因为内核要在内部遍历位图;epoll采用事件回调方式监控(谁就绪回调谁),因此效率不会因为fd增多而降低。
4.select没有直接返回就绪的文件描述符,需要用户对集合进行遍历判断;epoll直接返回的就绪事件结构,用户可以直接操作,不需要空遍历;
5.select监控的fd有限,默认是1024个;epoll监控的fd没有上限。
6.select可以跨平台;epoll不可以跨平台。
事件结构(epoll_event):
这里提一下poll:
相比于select: 1.接口使用更方便,采用事件结构数组进行所有事件监控,简化了三种集合操作
2.监控的fd没有上限
除了这两个优点,缺点和select一样。