select:
- select每次都会将用户态数据拷贝到内核态,包括三个fd_set和time_val,最后将更改后的数据从内核态重新拷贝到用户态,这也是select效率低下的原因之一。
- 参数time_val,传入的是指针类型,内核会根据时间对其进行修改。
- select利用等待队列让用户没有可读/写/异常时睡眠,有可读写异常时唤醒。(设置NULL则一直等待,超时时间为传入的值,中途会转换为时间周期计算)
- select的主要工作在do_select,将其进程的状态设置为可中断阻塞状态(睡眠阻塞状态),通过检测对文件描述符对应驱动程序的poll操作(检测文件描述符是否可以操作),轮询检测监视文件描述符的位图,每有一个时间则将retval++(select的返回值,就绪的文件描述符),如果有则将进程设置为运行态,没有则阻塞睡眠等待唤醒(schedule_timeout(__timeout))。
- 对于驱动程序poll,先将current挂载在等待队列上(此时不阻塞),直到将其遍历完成,等待唤醒,返回值是mask值。
- select共开辟了6个位图,前三个用作拷贝用户态的数据,后三个用来返回结果。
poll:
- select,poll对于poll_initwait(&table)初始化,定义了其回调函数_poll_wait。_poll_wait的作用即一次设备poll调用穿件一个poll_table_entry,并将current挂载在等待队列上,之后轮询所有fd对应的poll,等待唤醒,相比select传入fdset更高效。
- poll对于描述符建立了pollfd,使用链表将其相连,每个节点的大小就是页面大小,所以对于文件描述符没有限制,优先在栈上创建,不足的在堆上申请。但是参数传递和页面分配也十分影响效率。
- poll于select在数据拷贝方面一样,每次执行都需要重新拷贝监听的时间到内核态,底层实现也基本相同。
- select轮询到max+1 个,而poll监听fd在pollfd中的数组里,当描述符相对离散时,poll的效率略高。
epoll:
1.实现了epoll自己的文件结构eventpoll和epitem,在初始化时(操作系统启动时)分配相应的缓存,使用slab分配内存。epoll_creat返回的为新的文件描述符(仅epoll可用),可以通过file->private_data得到。
2.高效之处体现在不必每次从用户空间拷贝数据,通过ctl进行增量操作。
3.每次epoll_wait只返回就绪的描述符,同时有epoll_entry作为epitem的私有项(其中有等待队列),关联epitem和回掉函数(将就绪的epitem加入rdlist)。eventpoll的等待队列主要作用是将其唤醒。
4.epoll的ET模式,每次epoll_wait后将数据拷贝到txlist后将数据发送到用户空间,仅将没有标记为ET的和状态改变的拷贝回到rdlist。