1.文件描述符传入内核的方式
select:创建文件描述符集合,拷贝到内核,每个文件描述符会多次拷贝。
poll:创建struct pollfd结构体数组拷贝传入内核。
epoll:epoll_ctr一个一个上树,并建立回调关系,每个文件描述符只会传入一次。
2.内核检测文件描述符就绪的方式
select:轮询,遍历所有fd,最后返回一个是否就绪的mask掩码,并根据掩码给fd_set赋值
poll:轮询遍历,如果就绪,加入到就绪队列继续遍历
epoll:在epoll_ctr上树的时候就在fd和网络设备间建立了回调关系,当数据到达本地后,会触发回调函数,回调函数把事件加入到就绪队列
3.就绪文件描述符传出、用户区处理的方式
select:select()拷贝传出fd_set,并返回就绪个数;但对用户去而言,不知道是哪些fd就绪,需要在遍历这个fd_set,和传入前的fd_set进行比对。
poll:和select一样,拷贝传出struct pollfd数组,返回就绪个数;遍历这个数组。
epoll:epoll_wait()返回就绪个数,它是返回的就绪队列的数据,即返回的都是就绪的fd,只需一次遍历就行,每次遍历都是有用的。
4.重复监听的方式
select:重新组织fd_set,重新拷贝进去。
poll:重新组织struct pollfd数组,重新拷贝进去
epoll:还是沿用之前的那棵树,无需重新传入。
总结:
epoll效率高在传入不重复,传出够具体,内核监听用回调。
但是,epoll是在连接数很多,但是活跃数不太多的情况下高效。当连接数10000,活跃数10000时,epoll和select也没啥区别了甚至更差,因为这时会触发太多的回调函数。