TZ : 我是一个平凡的人,梦想开一家甜品店
一 : 科普一分钟
IO多路复用
是IO模式
的一种,是一种单线程处理多并发的IO
操作的方案,其他IO
操作方案分别有 :
- 阻塞 I/O(blocking IO)
- 非阻塞 I/O(nonblocking IO)
- I/O 多路复用( IO multiplexing)
- 异步 I/O(asynchronous IO)
IO多路复用
其实就是我们说的select
,poll
,epoll
,它的基本原理就是select
,poll
,epoll
这个function会不断的轮询所负责的所有socket
,当某个socket
有数据到达了,就通知用户进程。
select
会轮询检测所有的连接,或者 IO
,其理论所使用的就是事件驱动模型
这一编程范式
二 : 事件驱动模型与IO多路复用关系
-
事件驱动模型 :
好比中午全体员工1000人去饭,点餐完毕后我们回到座位,饭做好了服务员就会叫我们对应点餐的人去取餐.
目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:
- 有一个事件(消息)队列;
- 鼠标按下时,往这个队列中增加一个点击事件(消息);
- 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;
- 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;
IO多路复用
所用的编程范式就是事件驱动模型
,是一种监测
和回调
封装好的select
(拿select举例)会不断询问处理,我们所放入的IO事件,哪个有数据,就返回.我们就可以拿到了.
协程
也使用了事件驱动模型
来绕过IO
事件,遇到IO
事件放入操作系统的读取IO
列表,然后操作系统处理好给我们返回.
三 : 异步IO与同步IO
- 同步IO
1.阻塞 I/O(blocking IO)
2.非阻塞 I/O(nonblocking IO)
3.I/O 多路复用( IO multiplexing)
为什么会发生阻塞呢?其根本原因就是当数据准备好之后,内核态
向用户态
转发的时候呼叫进程来接受,这个转化的过程是耗时的操作,等转发结束后,阻塞接受
异步IO
为什么异步是没有阻塞的操作呢,其根本原因也是因为,在内核态
向用户态
转发结束后,才叫进程来接收,进程不用等待,直接拿到数据.同步异步区别
同步IO
是快餐性质
餐好了以后你过去取食物,拿食物回到座位,这个过程是耗时的操作.
异步IO
是大餐性质
,我们餐好了以后,大家还在座位上聊天,服务员就会把食物端到桌子上,然后我们吃就可以了,高端服务.
解析 :
我们可以用IO
多路复用写一个SocketServer
实现单线程下的大并发.
但是IO
多路复用本质是还是同步IO
,因为数据从内核态
到用户态
的拷贝需要等待操作系统完成,只有异步 I/O
本质是是异步IO
,本质上异步 I/O
会当数据从内核态
拷贝到用户态
这一过程完成之后再去通知程序直接取走,因为I/O操作
是系统完成,所以这才是真正意义上的异步IO操作.
四 : IO多路复用socketServer
利用IO多路复用
写一个socketServer
,大多数情况下机几乎很难用到,因为有许许多多模块和框架已经为我们封装好了,简单了解一下底层的实践即可.
# Author:TianTianBaby
import select
import socket
import queue
#创建socket连接
server = socket.socket()
server.bind(('localhost',9000))
server.listen(1000)
#设置非阻塞模式
server.setblocking(False)
#key 为接受消息对象实例,key 为接受的数据
mes_dic = {}
#需要检测的连接列表
inputs = [server,]
#返回上一次的数据列表
outputs = []
while True:
readable,writeable,exceptional = select.select(inputs,outputs,inputs)
for r in readable:
if r is server:#代表来了一个新连接
conn,addr = server.accept()
inputs.append(conn)#因为新建立的连接还没发数据过来,现在就接受的话程序就报错
#所以要想实现这个客户端发数据时server端能知道,就需要让select再监测
#初始化一个队列,后面存要返回给这个客户端的数据
mes_dic[conn] = queue.Queue()
else:
data = r.recv(1024)
mes_dic[r].put(data)
#放入返回的连接队列里
outputs.append(r)
#要返回给客户端的连接列表
for w in writeable:
data_to_client = mes_dic[w].get()
#返回给客户端源数据
w.send(data_to_client)
#确保下次循环的时候writeable,不反回这个已经处理完的连接
outputs.remove(w)
#异常
for e in exceptional:
if e in outputs:
outputs.remove(e)
inputs.remove(e)
del mes_dic[e]
五 : 总结
select
,poll
,epoll
都是IO多路复用
的机制。I/O多路复用
就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select
,poll
,epoll
本质上都是同步I/O
,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O
则无需自己负责进行读写,异步I/O
的实现会负责把数据从内核
拷贝到用户空间
。