IO模型分类

五种IO Model

blocking IO      阻塞IO

nonblocking IO     非阻塞IO

IO multiplexing     IO多路复用

signal driven IO    信号驱动IO

asynchronous IO    异步IO

signal driven IO(信号驱动IO)在实际中并不常用,所以只剩下四种IO Model。

网络IO的两个过程

对于一个network IO ,会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个阶段:

  • 等待数据准备 (Waiting for the data to be ready):等待系统接收数据
  • 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process):进程从系统缓存中拿到数据

同步IO:在这两个过程中有任意阶段出现阻塞状态。

  阻塞IO、非阻塞IO、IO多路复用都是同步IO

异步IO:全程无阻塞的IO

  异步IO属于异步IO(真的没毛病)

阻塞IO(Blocking IO)

UDP包:当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

blocking IO的特点就是在IO执行的两个阶段都被block了。

示例:

 #服务端
import socket
sock=socket.socket() #默认是TCP
sock.bind(("127.0.0.1",8088)) sock.listen(5)
while True:
conn,addr=sock.accept() #默认是就是阻塞的方式,监听等待客户端连接(阶段一):等待中的阻塞
#客户端连接后接收数据(阶段二):socket对象和客户端地址,虽然接收数据的过程很快但是实际上也是阻塞
while True:
data=conn.recv(1024) #也是两个阶段的阻塞
print(data.decode('utf8'))
if data.decode('utf8') =='q':
break
respnse=input('>>>>')
conn.send(respnse.encode('utf8')) #客户端
import socket
sock=socket.socket()
sock.connect(("127.0.0.1",8088)) while True:
data=input('>>>').strip()
sock.send(data.encode('utf8'))
s_data = sock.recv(1024) #两个阶段的阻塞
print(s_data.decode('utf8'))

非阻塞IO(Non-blocking IO)

当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。所以用户进程不需要等待,而是马上就得到了一个结果,用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。这个过程中,用户进程是需要不断的主动询问kernel数据好了没有。

非阻塞实际上是将大的整片时间的阻塞分成N多的小的阻塞,每次recvform系统调用之间,可以干点别的事情,然后再发起recvform系统调用,重复的过程通常被称之为轮询。轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态。

优点:能够在等待任务完成的时间里干其他活了(包括提交其他任务,也就是 “后台” 可以有多个任务在同时执行)。

缺点:任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。

 #服务端

 import socket
import time
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #默认是TCP
sock.bind(("127.0.0.1",8088))
sock.listen(5)
sock.setblocking(False) while True:
try:
print('server waiting')
conn, addr = sock.accept() # 默认是个阻塞的方式,等待客户端连接
while True:
data = conn.recv(1024) #这边也是阻塞的IO
print(data.decode('utf8'))
if data.decode('utf8') == 'q':
break
respnse = input('>>>>')
conn.send(respnse.encode('utf8'))
except Exception as e:
print (e)
time.sleep(4) #客户端
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #默认是TCP while True:
sock.connect(("127.0.0.1", 8088)) #因为服务端recv也是非阻塞,所以要不断重新连接
data=input('>>>').strip()
sock.send(data.encode('utf8'))
s_data = sock.recv(1024)
print(s_data.decode('utf8'))

IO多路复用IO multiplexing

IO多路复用,也叫做event driven IO,实现方式:select,poll或epoll

IO多路复用的好处就在于单个process就可以同时处理多个网络连接的IO

用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。这个过程中有两次system call(系统调用) select阻塞时候 和 recvfrom阻塞时候。用多路复用的的优势在于它可以同时处理大批量的connection,不适用单个或少量,少量还不如multi-threading + blocking IO。

select示例:

 #服务端
import socket
import select
sock=socket.socket()
sock.bind(("127.0.0.1",8088))
sock.listen(5) inp=[sock,] #定义监听的套接字对象列表,列表列表里可以有多个对象 while True:
#字段顺序:input list 、output list、error list、date(可以不写)
r=select.select(inp,[],[],None) #对比的是sock.accept(),这一步只做了监听的事情,监听哪个socket对象活动,当没有客户端连接时候会阻塞
# 当监听到有活动的socket对象时候,将返回值给r
print('r',r)
print('r',r[0])
#r接收的返回是一个元组,r[0]是活动的对象列表 for obj in r[0]:
if obj == sock: #如果活动的对象是sock,那么将客户端对象加入监听列表,客户端再发数据时候,触发客户端的对象活动
conn,addr=obj.accept() #accept只做第二个阶段的事情,取回数据:client的socket对象和地址
print(conn,addr)
inp.append(conn)
else:
data=obj.recv(1024)
print(data.decode('utf8'))
resp=input('>>>')
obj.send(resp.encode('utf8')) #客户端
import socket
sock=socket.socket()
sock.connect(("127.0.0.1", 8088))
while True:
data=input('>>>').strip()
sock.send(data.encode('utf8'))
s_data = sock.recv(1024)
print(s_data.decode('utf8'))

因为使用的是for循环,当多个客户端发消息给服务端,只能一个个顺序处理。

在windows下只能用select实现多路复用

在Linux可以使用select、poll、epoll实现,推荐使用epoll,对比:

  select和poll的监听方式为轮询方式,即每次都要循环一遍监听列表,效率低,另外select有连接数限制,poll无限

  epoll连接数无限,区别在于监听方式不同,每个socket对象绑定一个回调函数,当socket对象活动了就触发回调函数,把自己写到活动列表中,epoll直接调用活动列表

信号驱动IO(signal driven IO)

不常用,不做说明

异步IO(Asynchronous I/O)

用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

IO模型区别

selectors模块

该模块能够按照系统平台,自动选择多路复用的方式。

 #服务端
import selectors
import socket sel=selectors.DefaultSelector() def accept(sock,mask):
conn,addr=sock.accept() #4、获取客户端的conn对象和地址
print('accetped',conn,'from',addr)
conn.setblocking(False)
sel.register(conn,selectors.EVENT_READ,read) #5、注册conn对象,将conn对象和函数read绑定 def read(conn,mask):
data=conn.recv(1024) #9、服务端通过conn对象接收消息,进行下面的逻辑处理
if data:
print('echoing',repr(data),'to',conn)
conn.send(data)
else:
print('closing',conn)
sel.unregister(conn)
conn.close() sock=socket.socket()
sock.bind(('127.0.0.1',8088))
sock.listen(100)
sock.setblocking(False)
sel.register(sock,selectors.EVENT_READ,accept) #sock对象注册绑定accept函数 while True:
#不管是哪个方式,都是使用select方法监听活动的socket对象
events=sel.select() #1、执行sel阻塞监听,当有客户端连接,激活sock对象,返回一个存放活动sock对象相关信息的列表
#6、客户端通过conn对象发送消息,激活sel监听列表中的的conn对象,返回一个存放活动conn对象相关信息的列表
print(events,type(events))
for key,mask in events:
print(mask)
print(key.data) #socket对象注册绑定的accept函数
print(key.fileobj)
callback=key.data #2、取得返回的sock绑定的函数
#7、取得返回conn绑定的函数
callback(key.fileobj,mask) #3、key.fileobj是sock对象,执行函数
#8、执行函数read,并传入conn对象 #客户端
import socket
sock=socket.socket()
sock.connect(("127.0.0.1", 8088))
while True:
data=input('>>>').strip()
sock.send(data.encode('utf8'))
s_data = sock.recv(1024)
print(s_data.decode('utf8'))

Python开发基础-Day33 IO模型的更多相关文章

  1. Python文件基础操作(IO入门1)

    转载请标明出处: http://www.cnblogs.com/why168888/p/6422270.html 本文出自:[Edwin博客园] Python文件基础操作(IO入门1) 1. pyth ...

  2. python 全栈开发,Day44(IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)

    昨日内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yield,greenlet 遇到IO gevent: 检测到IO,能够使用greenlet实现自动切换,规避了IO阻 ...

  3. python基础27 -----python进程终结篇-----IO模型

    一.IO模型 1.IO模型分类 1.阻塞IO--------blocking IO 2.非阻塞IO------nonblocking IO 3. 多路复用IO------- multiplexing ...

  4. python基础(17)-IO模型&selector模块

    先说一下IO发生时涉及的对象和步骤.对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(k ...

  5. python基础之IO模型

    IO模型分类 五种IO Model blocking IO 阻塞IO nonblocking IO 非阻塞IO IO multiplexing IO多路复用 signal driven IO 信号驱动 ...

  6. python网络编程——网络IO模型

    1 网络IO模型介绍 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:    (1)同步阻塞IO(Blocking IO):即传统的IO模型.    (2)同步非阻塞IO(Non-bl ...

  7. Python socket编程之IO模型介绍(多路复用*)

    1.I/O基础知识 1.1 什么是文件描述符? 在网络中,一个socket对象就是1个文件描述符,在文件中,1个文件句柄(即file对象)就是1个文件描述符.其实可以理解为就是一个“指针”或“句柄”, ...

  8. 还在用Alpine作为你Docker的Python开发基础镜像?其实Ubuntu更好一点

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_173 一般情况下,当你想为你的Python开发环境选择一个基础镜像时,大多数人都会选择Alpine,为什么?因为它太小了,仅仅只有 ...

  9. Python并发编程之IO模型

    目录 IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) IO多路复用 异步IO IO模型比较分析 selectors模块 一.IO模型介绍 Stevens ...

随机推荐

  1. Python学习笔记(二十五)操作文件和目录

    摘抄:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319253241 ...

  2. JVM调优总结(3):垃圾回收面临的问题

    如何区分垃圾 上面说到的“引用计数”法,通过统计控制生成对象和删除对象时的引用数来判断.垃圾回收程序收集计数为0的对象即可.但是这种方法无法解决循环引用.所以,后来实现的垃圾判断算法中,都是从程序运行 ...

  3. Calendar 日期类介绍

    Calendar c = Calendar.getInstance();//创建实例 默认是当前时刻 c.get(Calendar.YEAR); c.get(Calendar.MONTH); c.ge ...

  4. mongoose使用简记

    mongodb中集合相当于表,常用指令 mongo 进入数据库 use yourdatabase 来选择你的数据集,这个跟关系型中的一样 show collections 来查看你数据集中的表,col ...

  5. Django之前端插件定制之表头

    什么是插件? 插件只是辅助,是开发过程中的一个阶段.一般项目一期会用各种插件,迅速将功能.界面搭出来,二期时就改成自己的代码了.大点的公司都有自己的js库,自己开发类似jquery的库. 那接下来就写 ...

  6. bzoj 3123 可持久化线段树启发式合并

    首先没有连边的操作的时候,我们可以用可持久化线段树来维护这棵树的信息,建立权值可持久化线段树,那么每个点继承父节点的线段树,当询问为x,y的时候我们可以询问rot[x]+rot[y]-rot[lca( ...

  7. Vue SPA 首屏加载优化实践

    写在前面 本文记录笔者在Vue SPA项目首屏加载优化过程中遇到的一些坑及优化方案! 我们以 vue-cli 工具为例,使用 vue-router 搭建SPA应用,UI框架选用 element-ui ...

  8. WebStorm 2016激活

    最近在网上找到一个激活webStorm 的好东西.博主说对jetbrains下的所有产品都是可以用这种方式激活的...如:PhpStorm,IntelliJ JDEA等. 但别的产品我没有试过.对于w ...

  9. docker之安装和基本使用(一)

    前言 开始折腾docker. 主要概念 容器:独立运行的一个或一组应用,与其他应用完全独立. 镜像:用于创建 Docker容器的模板. 仓库:用于收纳镜像文件,可以理解为代码控制中的代码仓库 注意: ...

  10. ProxySQL 监控和统计

    ProxySQL 监控和统计 很多有价值的统计数据在stats和monitor库中. admin@127.0.0.1 [(none)]>SHOW TABLES FROM stats; +---- ...