一:select(能监控数量有限,不能告诉用户程序具体那个连接有数据)
select目前几乎所有的平台都支持,其良好的跨平台支持也是一个优点
select的缺点在于单个进程能够监控的文件描述的数量存在最大限制(在liunx上一般是1024)
select监控socket连接是不能准确告诉用户是哪个
二:poll(和select一样,仅仅去除了最大监控数量)
poll和select在本质上没有太大差别,但是poll没有最大文件描述限制
三:epoll(不仅没有最大监控数量限制,还能告诉用户哪个连接有活跃)
epoll没有最大文件描述数量限制,还可以直接告诉用户程序是哪一个
四. epoll能实现高并发原理
- epoll() 中内核则维护一个链表,epoll_wait 直接检查链表是不是空就知道是否有文件描述符准备好了。
- 在内核实现中 epoll 是根据每个 sockfd 上面的与设备驱动程序建立起来的回调函数实现的。
- 某个 sockfd 上的事件发生时,与它对应的回调函数就会被调用,把这个 sockfd 加入链表。
- epoll上面链表中获取文件描述,这里使用内存映射(mmap)技术, 避免了复制大量文件描述符带来的开销
内存映射(mmap):内存映射文件,是由一个文件到一块内存的映射,将不必再对文件执行I/O操作
五.epoll和select,poll还有一个本质的区别的就是:
select 和 poll 只有在下次在循环回来,再去操作系统获取文件描述符
epoll 会直接告诉程序,我们这里已经就绪了,你可以接受数据了,等下一次协程去调用 epoll_wait 的时候就可以直接拿到就绪的文件描述符
IO操作
服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:
- (1)同步阻塞IO(Blocking IO) :即传统的IO模型
- (2)同步非阻塞IO(Non-blocking IO) :默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库
- (3)IO多路复用(IO Multiplexing) :即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型
- (4)异步IO(Asynchronous IO) :即经典的Proactor设计模式,也称为异步非阻塞IO
同步和异步的概念描述的是用户线程与内核的交互方式:
同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行
异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:
阻塞是指IO操作需要彻底完成后才返回到用户空间
非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成
I/O的实质是什么?
I/O的实质是将硬盘中的数据,或收到的数据实现从内核态 copy到 用户态的过程
用户态 & 内核态
系统空间分为两个部分,一部分是内核态,一部分是用户态的部分
内核态:内核态的空间资源只有操作系统能够访问
用户态:我们写的普通程序使用的空间
IO多路复用
IO多路复用:
- I/O是指网络I/O
- 多路指多个TCP连接(即socket或者channel)
- 复用指复用一个或几个线程
- 意思说一个或一组线程处理多个TCP连接
- 最大优势是减少系统开销,不必创建过多的进程/线程,也不必维护这些进程/线程
IO多路复用使用两个系统调用(select/poll/epoll和recvfrom)
- blocking IO只调用了recvfrom
- select/poll/epoll 核心是可以同时处理多个connection(连接)
- 多路复用模型中,每一个socket,设置为non-blocking,阻塞是被select这个函数block,而不是被socket阻塞的
六.猴子补丁
即在运行时对方法 / 类 / 属性 / 功能进行修改,把新的代码作为解决方案代替原有的程序,也就是为其打上补丁。
在使用gevent模块的使用会遇到猴子补丁
import gevent.monkey
gevent.monkey.patch_all()
注解:使用猴子补丁的方式,gevent能够修改标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和 select等模块,而变为协作式运行。也就是通过猴子补丁的monkey.patch_xxx()来将python标准库中模块或函数改成gevent中的响应的具有协程的协作式对象。这样在不改变原有代码的情况下,将应用的阻塞式方法,变成协程式的。