##epoll的工作模式:
1.LT模式(水平触发):若就绪的事件一次没有处理完要做的事件,就会一直去处理。即就会将没有处理完的事件继续放回到就绪队列之中(即那个内核中的链表),一直进行处理。
2.ET(边缘触发):就绪的事件只能处理一次,若没有处理完会在下次的其它事件就绪时再进行处理。而若以后再也没有就绪的事件,那么剩余的那部分数据也会随之而丢失。如果开发者使用了边缘触发的ET模式,那么需要我们开发者提高难度,要保证每次事件都能一次性处理完成,因为ET模式的epoll使用的是非阻塞模式的读写I/O。
##epoll的优点:
- epoll的时间复杂度是O(1),因为epoll的工作原理是一旦有事件流需要处理,epoll会告诉它的事件流类型和事件发生的fd,所以时间复杂度达到了最优。
- 2.在空间复杂度上,epoll也是只开销了O(1),因为从用户态到内核态或者是内核态到用户态他们都是通过共享同一块内存来进行读写(复制拷贝)。
- 3.ET模式下的epoll效率极高,因为它在读写I/O时使用了非阻塞模式,使得效率更高。
##Epoll同步多路IO复用
##Select 之多路复用I/O
1.从用户态将fd_set文件描述集合拷贝到内核态
2.内核态:注册回调函数 _pollwait();
3.内核态下,进行轮询fd_set,调用fd的tcp_poll或者udp_poll亦或者是datagram_poll.(tcp_poll的主要实现就是_pollwait())
4.__pollwait的主要工作就是把current(当前进程)挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll来说,其等待队列是sk->sk_sleep(注意把进程挂到等待队列中并不代表进程已经睡眠了)。在设备收到一条消息(网络设备)或填写完文件数据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了。
5.调用fd的poll方式会返回一个mask值,用于描述读写操作是否就绪,根据这个mask掩码给fd_set赋值(如若有就绪的fd,则进行相应的读写操作)。
6.如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout是调用select的进程(也就是current)进入睡眠。当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间(schedule_timeout指定),还是没人唤醒,则调用select的进程会重新被唤醒获得CPU,进而重新遍历fd,判断有没有就绪的fd。
7.把fd_set从内核空间拷贝到用户空间。##select的缺点: 1.从用户态拷贝fd_set到内核态,如果文件集合很大,会需要开销很多内存。 2.用内核态拷贝fd_set到用户态,如果文件集合很大,也会需要开销很大的内核内存。 3.每次从用户态拷贝fd_set到内核,内核都需要进行轮询遍历是否有可读可写的fd,那么时间复杂度都是O(n)。