select poll epoll之间的区别

时间:2022-04-17 06:46:39

1、select poll每次循环调用时都需要将文件描述符和事件拷贝到内核空间,epoll只需要拷贝一次;

(这种情况在对于描述符数量不大的情况下还可以,但是当描述符的数量达到十几万甚至上百万的时候,他们的效率就会急速降低,因为每一次轮询都需要将这些所有的socket描述符从用户态拷贝到内核态,会造成大量的浪费和资源开销)

2、select poll每次返回后,需要遍历所有描述符才能找到就绪的,因此它俩的时间复杂度为O(n),而epoll只需要O(1);

3、select poll内核通过轮询的方式完成,时间复杂度为O(n),而epoll是在每个文件描述符上设置回调函数,时间复杂度为O(1)。

(与select相比poll没有太多的区别,唯一的改进是使用了链表来保存fd,使得能够监听的数量远远超过了1024,但是对于将用户数据拷贝到内核空间,线性遍历fd这两个并没有太大的改变)

epoll的底层实现
利用sys_epoll_create()创建内核事件表,在sys_epoll_creat()里面创建了struct eventpoll结构体,其中包括两个成员:

就绪队列struct list_head rdlist,用来存放有就绪事件的描述符;

红黑树struct rb_root rbr,作为内核事件表,用来收集描述符;

每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会通过ep_instert挂载到红黑树上,这样重复添加的事件就可以通过红黑树而高效的识别出来;

而所有添加到epoll中的事件都会与驱动程序建立回调关系,当相应的事件发生时,会调用ep_poll_callback这个回调方法,它会将发生的事件添加到rdlist中;

在epoll中,对于每一个事件,都会建立一个epitem结构体,它里面包括:

红黑树节点
Rdlist节点
事件句柄信息
一个指向其所属的eventpoll对象的指针
期待发生的事件类型
当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist中是否有epitem元素即可。如果rdlist不为空,则把事件复制到用户态,同时将事件数量返回给用户;如果为空,就等待直到超时

通过分析可知:通过红黑树和双链表数据结构,并结合回调机制,造就了epoll的高效;

epoll的工作模式ET和LT
ET模式是高速模式,叫做边缘触发模式,LT模式是默认模式,叫做水平触发模式;

两种工作模式的区别:

ET模式:如果一个描述符上有数据到达,然后读取这个描述符上的数据,如果没有将数据读取完,当下次epoll_wait返回的时候这个描述符中的数据就再也读取不到了;

LT模式:如果对一个描述符的数据没有读取完成,那么下次当epoll_wait返回的时候会继续触发,也就可以继续获取到这个描述符,从而能够接着读;

实现方式:

当一个socket描述符的中断事件发生,内核会将数据从网卡复制到内核,同时将1socket描述符插入到rddlist中,如果此时调用了epoll_wait会把rdlist中就绪的socket描述符复制到用户空间,然后清理掉这个rdlist中的数据,最后epoll_wait还会再次检查这些socket描述符。如果是工作在LT模式下,并且这些socket描述符上还有数据没有读取完,那么LT就会再次把没有读完的socket描述符放入到rdlist中,所以再次调用epoll_wait的时候会再次触发。