select、poll和epoll
- select
通过一个select()系统调用来见识多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。
select优缺点:
-
优点:目前几乎支持所有平台,优点是良好的跨平台支持;
-
缺点:
- 单个进程能够见识的文件描述符存在最大限制,linux一般是1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制;
- select()说维护的存储大量文件描述符的数据结构,随着文件描述符的数量增大,其复制开销也线性增长;
- 网络响应时间的延迟使得大量TCP链接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费一定的开销。
-
poll
和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。
poll的优缺点:
-
优点:select()和poll()将就绪的文件描述符告诉进程后,不管进程有没有对其进行IO操作,再次调用的时候将会报告这些文件的描述符,就绪信息不会丢失;
-
缺点:包含大量的文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大;
-
epoll
epoll是在2.6内核中提出的,是之前的select和poll的增强版本,同时支持水平触发和边缘触发。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
epoll采用基于事件的就绪通知方式,在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
# 基于select实现的socket服务端
import socket
import select
# 创建套接字对象
sk = socket.socket()
# 绑定套接字
sk.bind