IO 多路复用
IO密集型程序 : 在程序执行过程中存在大量IO操作,而cpu运算操作较少。消耗cpu较少,运行效率较低
计算密集型程序(cpu密集型程序):在程序执行中cpu运算较多,IO操作相对较少。消耗cpu大,运行速度快
IO分类:
- 阻塞IO
- 非阻塞IO
- IO多路复用
阻塞IO:是IO的默认形态,是效率较低的一种IO情形。
阻塞情况:
-
因为某种条件没有达成造成的阻塞
e.g. accept input recv
-
处理IO数据传输时间较长形成的阻塞
e.g. 网络传输过程,文件读写过程
非阻塞IO :通过修改IO事件的属性,使其变为非阻塞状态。比如:超时检测
*非阻塞IO往往和循环判断一起使用
IO多路复用
定义 : 同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。以此形成可用同时操作多个IO的并发行为,避免一个IO阻塞,造成所有IO都无法执行。
IO多路复用的编程实现
- 将IO设置为关注IO
- 将关注IO提交给内核监测
- 处理内核给我们反馈准备就绪的IO
具体方案:
select ——> windows linux unix
poll ——> linux unix
epoll ——> linux unix
from select import *
rs,ws,xs = select(rlist, wlist, xlist[, timeout])
功能: 监控IO事件,阻塞等待IO事件发生
参数: rlist 列表 存放我们监控等待处理的IO事件
wlist 列表 存放我们要主动操作的IO事件
xlist 列表 我们要关注出错处理的IO事件
timeout 超时时间
返回值:rs 列表 rlist中准备就绪的IO
ws 列表 wlist中准备就绪的IO
xs 列表 xlist中准备就绪的IO
注意 : 1. rlist有已经准备就绪的IO事件,select会立即返回给rs
2. IO多路复用占用计算机资源少,io效率高
poll
1.创建poll对象
p = poll()
2.添加注册事件
p.register(s,POLLIN | POLLERR)
POLLIN POLLOUT POLLERR POLLHUP POLLNVAL
rlist wlist xlist 断开 无效数据
p.unregister(s) 从关注事件中移除
3. 阻塞等待IO发生
events = p.poll()
功能 : 阻塞等待IO发生
返回值 : events 是一个列表,列表中给每一个元素都是一个元组,代表一个发生的IO事件
[(fileno,event),(fileno,event),(fileno,event)...]
fileno: 就绪IO的文件描述符
event: 具体就绪IO事件
* 需要通过文件描述符(fileno)找到对应的IO对象,所以需要建立映射关系 {s.fileno() : s}
4. 处理具体的IO
epoll方法
使用方法:基本与poll方法相同
epoll 和 poll的区别:
1. epoll 的效率要比 poll和select 高
2. epoll 的事件触发方式更多
from socket import *
from select import *
#创建套接字作为我们关注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)
#创建poll对象
p = poll()
#fileno ---> IO对象的字典
fdmap = {s.fileno():s}
#注册关注的IO
p.register(s,POLLIN | POLLERR)
while True:
#进行IO监控
events = p.poll()
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print("Connect from",addr)
#添加新的关注事件
p.register(c,POLLIN | POLLHUP)
fdmap[c.fileno()] = c
elif event & POLLIN:
data = fdmap[fd].recv(1024)
if not data:
#客户端退出,从关注事件移除
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
else:
print(data.decode())
fdmap[fd].send(b'Receive')