概述
Python 中的 select 模块专注于 I / O 多路复用,提供了 select poll epoll 三个方法(其中后两个在 Linux 中可用,windows 仅支持 select),另外也提供了 kqueue 方法(freeBSD 系统),下面重点给大家介绍 select、epoll 方法
select 方法
进程指定内核监听哪些文件描述符 (最多监听 1024 个 fd) 的哪些事件,当没有文件描述符事件发生时,进程被阻塞;当一个或者多个文件描述符事件发生时,进程被唤醒。
当我们调用 select()时:
1 上下文切换转换为内核态
2 将 fd 从用户空间复制到内核空间
3 内核遍历所有 fd,查看其对应事件是否发生
4 如果没发生,将进程阻塞,当设备驱动产生中断或者 timeout 时间后,将进程唤醒,再次进行遍历
5 返回遍历后的 fd
6 将 fd 从内核空间复制到用户空间
注:fd 为文件描述符
epoll 方法
epoll 很好的改进了 select:
1 epoll 的解决方案在 epoll_ctl 函数中。每次注册新的事件到 epoll 句柄中时,会把所有的 fd 拷贝进内核,而不是在 epoll_wait 的时候重复拷贝。epoll 保证了每个 fd 在整个过程中只会拷贝一次。
2 epoll 会在 epoll_ctl 时把指定的 fd 遍历一遍(这一遍必不可少)并为每个 fd 指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的 fd 加入一个就绪链表。epoll_wait 的工作实际上就是在这个就绪链表中查看有没有就绪的 fd
3 epoll 对文件描述符没有额外限制
水平触发和边缘触发:
Level_triggered(水平触发,有时也称条件触发):当被监控的文件描述符上有可读写事件发生时,epoll.poll()会通知处理程序去读写。如果这次没有把数据一次性全部读写完 (如读写缓冲区太小),那么下次调用 epoll.poll() 时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!优点很明显:稳定可靠
Edge_triggered(边缘触发,有时也称状态触发):当被监控的文件描述符上有可读写事件发生时,epoll.poll()会通知处理程序去读写。如果这次没有把数据全部读写完 (如读写缓冲区太小),那么下次调用 epoll.poll() 时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!缺点:某些条件下不可靠
更多技术资讯可关注:gzitcast