python基础之IO模型

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了。

示例:

 1 #服务端
 2 import socket
 3 sock=socket.socket()   #默认是TCP
 4 sock.bind(("127.0.0.1",8088))
 5
 6 sock.listen(5)
 7 while True:
 8     conn,addr=sock.accept()   #默认是就是阻塞的方式,监听等待客户端连接(阶段一):等待中的阻塞
 9                               #客户端连接后接收数据(阶段二):socket对象和客户端地址,虽然接收数据的过程很快但是实际上也是阻塞
10     while True:
11         data=conn.recv(1024)    #也是两个阶段的阻塞
12         print(data.decode(‘utf8‘))
13         if data.decode(‘utf8‘) ==‘q‘:
14             break
15         respnse=input(‘>>>>‘)
16         conn.send(respnse.encode(‘utf8‘))
17
18
19 #客户端
20 import socket
21 sock=socket.socket()
22 sock.connect(("127.0.0.1",8088))
23
24 while True:
25     data=input(‘>>>‘).strip()
26     sock.send(data.encode(‘utf8‘))
27     s_data = sock.recv(1024)    #两个阶段的阻塞
28     print(s_data.decode(‘utf8‘))

非阻塞IO(Non-blocking IO)

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

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

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

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

 1 #服务端
 2
 3 import socket
 4 import time
 5 sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)   #默认是TCP
 6 sock.bind(("127.0.0.1",8088))
 7 sock.listen(5)
 8 sock.setblocking(False)
 9
10 while True:
11     try:
12         print(‘server waiting‘)
13         conn, addr = sock.accept()  # 默认是个阻塞的方式,等待客户端连接
14         while True:
15             data = conn.recv(1024)  #这边也是阻塞的IO
16             print(data.decode(‘utf8‘))
17             if data.decode(‘utf8‘) == ‘q‘:
18                 break
19             respnse = input(‘>>>>‘)
20             conn.send(respnse.encode(‘utf8‘))
21     except Exception as e:
22         print (e)
23         time.sleep(4)
24
25 #客户端
26 import socket
27 sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)   #默认是TCP
28
29 while True:
30     sock.connect(("127.0.0.1", 8088))   #因为服务端recv也是非阻塞,所以要不断重新连接
31     data=input(‘>>>‘).strip()
32     sock.send(data.encode(‘utf8‘))
33     s_data = sock.recv(1024)
34     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示例:

 1 #服务端
 2 import socket
 3 import select
 4 sock=socket.socket()
 5 sock.bind(("127.0.0.1",8088))
 6 sock.listen(5)
 7
 8 inp=[sock,] #定义监听的套接字对象列表,列表列表里可以有多个对象
 9
10 while True:
11     #字段顺序:input list 、output list、error list、date(可以不写)
12     r=select.select(inp,[],[],None) #对比的是sock.accept(),这一步只做了监听的事情,监听哪个socket对象活动,当没有客户端连接时候会阻塞
13     # 当监听到有活动的socket对象时候,将返回值给r
14     print(‘r‘,r)
15     print(‘r‘,r[0])
16     #r接收的返回是一个元组,r[0]是活动的对象列表
17
18     for obj in r[0]:
19         if obj == sock: #如果活动的对象是sock,那么将客户端对象加入监听列表,客户端再发数据时候,触发客户端的对象活动
20             conn,addr=obj.accept()  #accept只做第二个阶段的事情,取回数据:client的socket对象和地址
21             print(conn,addr)
22             inp.append(conn)
23         else:
24             data=obj.recv(1024)
25             print(data.decode(‘utf8‘))
26             resp=input(‘>>>‘)
27             obj.send(resp.encode(‘utf8‘))
28
29 #客户端
30 import socket
31 sock=socket.socket()
32 sock.connect(("127.0.0.1", 8088))
33 while True:
34     data=input(‘>>>‘).strip()
35     sock.send(data.encode(‘utf8‘))
36     s_data = sock.recv(1024)
37     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模块

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

 1 #服务端
 2 import selectors
 3 import socket
 4
 5 sel=selectors.DefaultSelector()
 6
 7 def accept(sock,mask):
 8     conn,addr=sock.accept()     #4、获取客户端的conn对象和地址
 9     print(‘accetped‘,conn,‘from‘,addr)
10     conn.setblocking(False)
11     sel.register(conn,selectors.EVENT_READ,read)    #5、注册conn对象,将conn对象和函数read绑定
12
13 def read(conn,mask):
14     data=conn.recv(1024)    #9、服务端通过conn对象接收消息,进行下面的逻辑处理
15     if data:
16         print(‘echoing‘,repr(data),‘to‘,conn)
17         conn.send(data)
18     else:
19         print(‘closing‘,conn)
20         sel.unregister(conn)
21         conn.close()
22
23 sock=socket.socket()
24 sock.bind((‘127.0.0.1‘,8088))
25 sock.listen(100)
26 sock.setblocking(False)
27 sel.register(sock,selectors.EVENT_READ,accept)  #sock对象注册绑定accept函数
28
29 while True:
30     #不管是哪个方式,都是使用select方法监听活动的socket对象
31     events=sel.select()     #1、执行sel阻塞监听,当有客户端连接,激活sock对象,返回一个存放活动sock对象相关信息的列表
32                             #6、客户端通过conn对象发送消息,激活sel监听列表中的的conn对象,返回一个存放活动conn对象相关信息的列表
33     print(events,type(events))
34     for key,mask in events:
35         print(mask)
36         print(key.data)   #socket对象注册绑定的accept函数
37         print(key.fileobj)
38         callback=key.data   #2、取得返回的sock绑定的函数
39                             #7、取得返回conn绑定的函数
40         callback(key.fileobj,mask)  #3、key.fileobj是sock对象,执行函数
41                                     #8、执行函数read,并传入conn对象
42
43
44 #客户端
45 import socket
46 sock=socket.socket()
47 sock.connect(("127.0.0.1", 8088))
48 while True:
49     data=input(‘>>>‘).strip()
50     sock.send(data.encode(‘utf8‘))
51     s_data = sock.recv(1024)
52     print(s_data.decode(‘utf8‘))
时间: 2024-11-07 23:55:39

python基础之IO模型的相关文章

Python开发基础-Day33 IO模型

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),另一个就是系统内核

python并发编程&IO模型

一 IO模型介绍 为了更好地了解IO模型,可先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别? 这个问题其实不同的人给出的答案都可能不同,比如wiki,就认为asynchronous IO和non-blocking IO是一个东西. 这其实是因为不同的人的知识背景不同,并且在讨论这个问题的时候上下文(context)也不相同.所以,为了

5月2日 python学习总结 IO模型

IO模型 1.阻塞IO 2.非阻塞IO 3.多路复用IO 4.异步IO 一.阻塞IO blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了. 实际上,除非特别指定,几乎所有的IO接口 ( 包括socket接口 ) 都是阻塞型的. 所谓阻塞型接口是指系统调用(一般是IO接口)不返回调用结果并让当前线程一直阻塞,只有当该系统调用获    得结果或者超时出错时才返回. 在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大

python基础学习23----IO模型(简)

对于一个网络IO(network IO),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel).当一个read操作发生时,该操作会经历两个阶段: 1.等待数据准备 2.将数据从系统内核拷贝到进程当中 当收到数据后,这些数据会先存放到系统所用的内存当中,之后在由系统将数据从内核中拷贝到使用的进程当中 不同的IO模型的区别就在于上述的两个阶段 一.阻塞IO  (blocking IO) recvfrom进行系统调用后,等待数据和拷贝数

Python基础之 Django模型

本章节主要是包括通过Python安装Mysql驱动(mysqlclient),通过Django创建app,更新数据库模型. 1.安装 mysql 驱动.如果你没安装 mysql 驱动,可以执行以下命令安装: pip install mysqlclient 2.通过Django创建项目.可以执行以下命令安装: python django-admin.py startproject testModel 3.进入创建项目,通过Django创建app.可以执行以下命令安装: python  django

[Python基础]006.IO操作

IO操作 输入输出 print raw_input input 文件 打开文件 关闭文件 读文件 写文件 文件指针 实例 输入输出 输入输出方法都是Python的内建函数,并且不需要导入任何的包就可以使用. print 简单控制台输出方法 print ... print 'content' raw_input 简单的控制台输入方法 raw_input(显示的内容....) num = raw_input('Please input a number:') // 输入一个数字 print num

Python并发编程-IO模型-非阻塞IO实现SocketServer

Server.py import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.setblocking(False) #把socket中所有需要阻塞的方法都设为非阻塞IO, recv,accept, recvfrom sk.listen() conn_l = [] #保存所有来请求server端conn连接 del_conn = [] #用来存储所有已经与server端断开的conn while True: try: con

Python并发编程-IO模型-IO多路复用实现SocketServer

Server.py import select import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.setblocking(False) sk.listen() read_lst = [sk] #select监听谁就放入list while True: #[sk,conn] r_lst,w_lst,x_lsx = select.select(read_lst,[],[]) # print('*******',r_ls

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

一.IO模型 1.IO模型分类 1.阻塞IO--------blocking IO 2.非阻塞IO------nonblocking IO 3. 多路复用IO------- multiplexing 4.信号驱动IO-------signal driven IO (工作中不会使用到,只是作为了解) 5.异步IO------- asynchronous IO 2.通常情况下IO默认操作分为两个阶段(默认都是阻塞IO) 1.准备等待数据阶段,相当于请求操作系统是否有数据发送过来(调用IO操作). 2