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. 1 #服务端
  2. 2 import socket
  3. 3 sock=socket.socket() #默认是TCP
  4. 4 sock.bind(("127.0.0.1",8088))
  5. 5
  6. 6 sock.listen(5)
  7. 7 while True:
  8. 8 conn,addr=sock.accept() #默认是就是阻塞的方式,监听等待客户端连接(阶段一):等待中的阻塞
  9. 9 #客户端连接后接收数据(阶段二):socket对象和客户端地址,虽然接收数据的过程很快但是实际上也是阻塞
  10. 10 while True:
  11. 11 data=conn.recv(1024) #也是两个阶段的阻塞
  12. 12 print(data.decode('utf8'))
  13. 13 if data.decode('utf8') =='q':
  14. 14 break
  15. 15 respnse=input('>>>>')
  16. 16 conn.send(respnse.encode('utf8'))
  17. 17
  18. 18
  19. 19 #客户端
  20. 20 import socket
  21. 21 sock=socket.socket()
  22. 22 sock.connect(("127.0.0.1",8088))
  23. 23
  24. 24 while True:
  25. 25 data=input('>>>').strip()
  26. 26 sock.send(data.encode('utf8'))
  27. 27 s_data = sock.recv(1024) #两个阶段的阻塞
  28. 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. 1 #服务端
  2. 2
  3. 3 import socket
  4. 4 import time
  5. 5 sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #默认是TCP
  6. 6 sock.bind(("127.0.0.1",8088))
  7. 7 sock.listen(5)
  8. 8 sock.setblocking(False)
  9. 9
  10. 10 while True:
  11. 11 try:
  12. 12 print('server waiting')
  13. 13 conn, addr = sock.accept() # 默认是个阻塞的方式,等待客户端连接
  14. 14 while True:
  15. 15 data = conn.recv(1024) #这边也是阻塞的IO
  16. 16 print(data.decode('utf8'))
  17. 17 if data.decode('utf8') == 'q':
  18. 18 break
  19. 19 respnse = input('>>>>')
  20. 20 conn.send(respnse.encode('utf8'))
  21. 21 except Exception as e:
  22. 22 print (e)
  23. 23 time.sleep(4)
  24. 24
  25. 25 #客户端
  26. 26 import socket
  27. 27 sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #默认是TCP
  28. 28
  29. 29 while True:
  30. 30 sock.connect(("127.0.0.1", 8088)) #因为服务端recv也是非阻塞,所以要不断重新连接
  31. 31 data=input('>>>').strip()
  32. 32 sock.send(data.encode('utf8'))
  33. 33 s_data = sock.recv(1024)
  34. 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. 1 #服务端
  2. 2 import socket
  3. 3 import select
  4. 4 sock=socket.socket()
  5. 5 sock.bind(("127.0.0.1",8088))
  6. 6 sock.listen(5)
  7. 7
  8. 8 inp=[sock,] #定义监听的套接字对象列表,列表列表里可以有多个对象
  9. 9
  10. 10 while True:
  11. 11 #字段顺序:input list 、output list、error list、date(可以不写)
  12. 12 r=select.select(inp,[],[],None) #对比的是sock.accept(),这一步只做了监听的事情,监听哪个socket对象活动,当没有客户端连接时候会阻塞
  13. 13 # 当监听到有活动的socket对象时候,将返回值给r
  14. 14 print('r',r)
  15. 15 print('r',r[0])
  16. 16 #r接收的返回是一个元组,r[0]是活动的对象列表
  17. 17
  18. 18 for obj in r[0]:
  19. 19 if obj == sock: #如果活动的对象是sock,那么将客户端对象加入监听列表,客户端再发数据时候,触发客户端的对象活动
  20. 20 conn,addr=obj.accept() #accept只做第二个阶段的事情,取回数据:client的socket对象和地址
  21. 21 print(conn,addr)
  22. 22 inp.append(conn)
  23. 23 else:
  24. 24 data=obj.recv(1024)
  25. 25 print(data.decode('utf8'))
  26. 26 resp=input('>>>')
  27. 27 obj.send(resp.encode('utf8'))
  28. 28
  29. 29 #客户端
  30. 30 import socket
  31. 31 sock=socket.socket()
  32. 32 sock.connect(("127.0.0.1", 8088))
  33. 33 while True:
  34. 34 data=input('>>>').strip()
  35. 35 sock.send(data.encode('utf8'))
  36. 36 s_data = sock.recv(1024)
  37. 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. 1 #服务端
  2. 2 import selectors
  3. 3 import socket
  4. 4
  5. 5 sel=selectors.DefaultSelector()
  6. 6
  7. 7 def accept(sock,mask):
  8. 8 conn,addr=sock.accept() #4、获取客户端的conn对象和地址
  9. 9 print('accetped',conn,'from',addr)
  10. 10 conn.setblocking(False)
  11. 11 sel.register(conn,selectors.EVENT_READ,read) #5、注册conn对象,将conn对象和函数read绑定
  12. 12
  13. 13 def read(conn,mask):
  14. 14 data=conn.recv(1024) #9、服务端通过conn对象接收消息,进行下面的逻辑处理
  15. 15 if data:
  16. 16 print('echoing',repr(data),'to',conn)
  17. 17 conn.send(data)
  18. 18 else:
  19. 19 print('closing',conn)
  20. 20 sel.unregister(conn)
  21. 21 conn.close()
  22. 22
  23. 23 sock=socket.socket()
  24. 24 sock.bind(('127.0.0.1',8088))
  25. 25 sock.listen(100)
  26. 26 sock.setblocking(False)
  27. 27 sel.register(sock,selectors.EVENT_READ,accept) #sock对象注册绑定accept函数
  28. 28
  29. 29 while True:
  30. 30 #不管是哪个方式,都是使用select方法监听活动的socket对象
  31. 31 events=sel.select() #1、执行sel阻塞监听,当有客户端连接,激活sock对象,返回一个存放活动sock对象相关信息的列表
  32. 32 #6、客户端通过conn对象发送消息,激活sel监听列表中的的conn对象,返回一个存放活动conn对象相关信息的列表
  33. 33 print(events,type(events))
  34. 34 for key,mask in events:
  35. 35 print(mask)
  36. 36 print(key.data) #socket对象注册绑定的accept函数
  37. 37 print(key.fileobj)
  38. 38 callback=key.data #2、取得返回的sock绑定的函数
  39. 39 #7、取得返回conn绑定的函数
  40. 40 callback(key.fileobj,mask) #3、key.fileobj是sock对象,执行函数
  41. 41 #8、执行函数read,并传入conn对象
  42. 42
  43. 43
  44. 44 #客户端
  45. 45 import socket
  46. 46 sock=socket.socket()
  47. 47 sock.connect(("127.0.0.1", 8088))
  48. 48 while True:
  49. 49 data=input('>>>').strip()
  50. 50 sock.send(data.encode('utf8'))
  51. 51 s_data = sock.recv(1024)
  52. 52 print(s_data.decode('utf8'))

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

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

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

  2. Python之阻塞IO模型与非阻塞IO模型

    Python之阻塞IO模型与非阻塞IO模型 IO模型 1 阻塞IO: 全程阻塞 2 非阻塞IO: 发送多次系统调用: 优点:wait for data时无阻塞 缺点:1 系统调用太多 2 数据不是实时 ...

  3. python 并发编程 io模型 目录

    python 并发编程 IO模型介绍 python 并发编程 socket 服务端 客户端 阻塞io行为 python 并发编程 阻塞IO模型 python 并发编程 非阻塞IO模型 python 并 ...

  4. Python开发基础-Day33 IO模型

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

  5. Day14 - Python基础14 事件驱动模型、IO模型

    本节内容: 1:事件驱动模型 2:IO模型前戏准备 3:4种IO模型 1:事件驱动模型 传统的编程是如下线性模式的: 开始--->代码块A--->代码块B--->代码块C---> ...

  6. python并发之IO模型(二)

    blocking IO (阻塞IO) 在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样: 当用户进程调用了recvfrom这个系统调用,kernel就开 ...

  7. python并发之IO模型(一)

    事件驱动模型 协程:遇到IO操作就切换. 但什么时候切回去呢?怎么确定IO操作完了? 很多程序员可能会考虑使用“线程池”或“连接池”.“线程池”旨在减少创建和销毁线程的频率,其维持一定合理数量的线程, ...

  8. 1、http协议基础及IO模型

    Nginx (web server,web reverse proxy): http协议:80/tcp,HyperText Transfer Procotol http协议版本: HTTP/0.9:原 ...

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

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

随机推荐

  1. ThinkPHP5.0更改框架的验证方法:对象->validate(true)->save();

    我们更希望看到: // 新增对象至数据表 $result = $Teacher->validate(true)->save(); 而不是: // 新增对象至数据表 $result = $T ...

  2. IOS 播放视频(MPMoviePlayerController、MPMoviePlayerViewController)

    ● iOS提供了叫 做MPMoviePlayerController.MPMoviePlayerViewController的两个 类,可以用来轻松播放视频 ➢ YouTobe就是用MPMoviePl ...

  3. ACM-ICPC(11/9)

    今天看了一下黑书,感觉很刘汝佳,是他的风格,题目挺好的~~~ 枚举 P12翻硬币 二进制枚举每一列的情况2^9种. 在每一种情况下然后对于每一行就是翻与不翻的两种情况~~~ 贪心 P13钓鱼问题 PO ...

  4. 记忆化搜索,FatMouse and Cheese

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1107 http://acm.hdu.edu.cn/showpro ...

  5. 郑州Day6

    今天考了毕姥爷的一套题,差点保龄 题目 挺良心的一套题,至少我不用再搬一遍题面了 T1.B君的第一题 我为什么当时去写了一个树形\(dp\)还妄图\(A\)掉啊 这题保龄感觉舒爽 首先如果我们要求的是 ...

  6. 2018.11.7 关于将Web项目部署到阿里云服务器-5个步骤搞定

    将Eclipse导出的War包部署到阿里云服务器上,提供给移动端实时的访问 1. 先登录阿里云网站注册账号,选择服务器类型(我用的是 云服务器ECS), 如果你还是在读大学生可享受优惠价,最低好像是9 ...

  7. E: Unable to locate package

    E: Unable to locate package apt-get不能定位到包,有两种情况,一种是自己输入的包名字错误,确实找不到.另一种可能是执行sudo apt-get install之前更换 ...

  8. 【Calculus 微积分の一些个人理解】

    微积分 微积分(Calculus)是高等数学中研究函数的微分(Differentiation).积分(Integration)以及有关概念和应用的数学分支.它是数学的一个基础学科.内容主要包括极限.微 ...

  9. js关于密码框强弱度的提示

    三种密码强度的正则表达式: 较弱:全是数字或全是字母 6-16个字符:/^[0-9]{6,16}$|^[a-zA-Z]{6,16}$/; 中级:数字.26个英文字母 6-16个字符: /^[A-Za- ...

  10. 《深入理解Spring Cloud与微服务构建》书籍目录

    转载请标明出处: https://blog.csdn.net/forezp/article/details/79735542 本文出自方志朋的博客 作者简介 方志朋,毕业于武汉理工大学,CSDN博客专 ...