标签:io
事件驱动编程思想范式: 一种写代码的方式 ,这里程序的执行是由外部事件来决定的。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。
触发事件发送到队列,然后提取事件任务,发送到执行任务函数
当CPU指令集为 0的时候对应的是内核态(拥有硬件,软件所有权限), 1为用户态
用户态: 用户所运行的程序,
内核态: 操作系统
进程阻塞:正在执行的进程,由于期待的事件未发生,如请求系统资源失败,等待某种操作的完成,则由系统自动执行阻塞,使进程进入阻塞状态, 进程阻塞是不占用CPU资源的
缓存I/O : 标准IO操作数据流向路径:数据——流缓存区——内核缓存区——磁盘
数据--> 用户态 --> 内核态 --> 对端 内核态 --> 用户态 --> 对应应用程序 --> 数据
IO模型
阻塞
非阻塞
IO多路复用
异步IO
1、阻塞IO 全程阻塞 缺点:CPU不能做其它事情 优点: 数据同步
例: 比如以socket为例,当服务启动之后,accept发起一个系统调用,由用户态到内核态,操作系统(内核态)一直等待数据(程序阻塞),当启动client端连接到s端,内核态接收到数据,数据从内核态复制到用户态,最后返回给conn
缺点: 阻塞跟同步类似,都是你发我收, 我发你收 ,CPU将会一直阻塞,
##### 阻塞IO
'''IO 每次连接都只能连接一次,如果有其它客户端需要连接就需要等待本次连接断开'''
# server.py import socket so=socket.socket(socket.AF_INET,socket.SOCK_STREAM) ipport=('127.0.0.1',9001) so.bind(ipport) so.listen(5) while True: conn,data=so.accept() print(conn)
2、非阻塞IO
setblocking(False)
例: 服务端发起系统调用,查看内核态是否有数据,如果有就直接返回,如果也直接返回但会隔一段时间就会重新再去内核态在查看。
缺点: 系统调用发送太多,占据大量的数据资源, 当数据在前1秒发送时,而服务端正在阻塞就会导致数据无法及时处理
##### 非阻塞IO
''' setblocking socket等待用户进行连接,如果没有客户端进行连接,将会每隔一段时间查询一次内核态里是否有数据 '''
import socket import time so=socket.socket(socket.AF_INET,socket.SOCK_STREAM) ipport=('127.0.0.1',9001) so.bind(ipport) so.listen(5) so.setblocking(False) while True: try: conn,data=so.accept() print(conn) except Exception as F: print(F) time.sleep(5)
# 同阻塞client.py
3、IO多路调用
select.select([bindname,],[],[],5)
input output errorput 每隔几秒钟监听
select发起系统调用,内核态当发现有数据时返回给select,然后server端再发送一次recvfrom
##### IO多路复用 server端 import socket import selectors import json ipport=('127.0.0.1',9001) sel=selectors.DefaultSelector() sock=socket.socket() sock.bind(ipport) sock.listen(5) class UpDown: def put(self,obj,**data): print('ok') print(data) obj.send('ok'.encode('utf-8')) def accept(obj,mask): # 与客户端建立连接 就跟socket 配置的 socket.accept是一个意思,只不过这里配置的是异步io可以同步连接多个客户端 conn,addr=obj.accept() # print('client informaster: ',conn,'client addr: ',addr) # 注册客户端conn文件描述符对象,并绑定read函数 sel.register(conn,selectors.EVENT_READ,read) def read(obj,mask): try: data = obj.recv(1024) obj.send(data) except Exception as E: sel.unregister(obj) obj.close() sel.register(sock,selectors.EVENT_READ,accept) while 1: # 监听 events=sel.select() # 如果没有客户端连接就是为空 # 客户端第一次连接获取的是sock对象,绑定accept函数并执行, # 客户端发送数据 绑定read函数再进行数据的接收或发送操作 for key,mask in events: print(key.data) # 获取socket文件描述符,并获取register注册函数accept (sel.register(sock, selectors.EVENT_READ,accept)) conn=key.data # key.fileobj方法 获取的是客户端的socket文件描述符对象 <socket.socket fd=268, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9001)> # mask不知道有啥用 可以不配置它 conn(key.fileobj,mask)
# 客户端 (只实现了简单的发送接收,错误处理没弄)
import socket so=socket.socket(socket.AF_INET,socket.SOCK_STREAM) ipport=('127.0.0.1',9001) so.connect(ipport) while True: inp=input('>>>>>: ') so.send(inp.encode('utf-8')) data=so.recv(1024) print(data.decode('utf-8'))
触发方式: 两种
1、 水平触发
只有高电平(1)或低电平(0)时才触发通知,只要在这两种状态就能得到通知,上面提到的只要有数据可读(描述符就绪)那么水平触发的epooll就立即返回
2、 边缘触发
只有电平发生变化(高电平到低电平,或电低平到高电平)的时候才触发通知,
3、IO多路复用优势:同时可以监听多个连接
IO多路复用: 单线程下实现的并发, 原理:利用IO空闲时间
select: 效率最慢 windows下只有这个, linux三个都有,最大量只有1024个连接
poll:
epoll: 效率最快,
只要有一点点阻塞就是同步IO
4、异步IO
异步最大特点: 全程无阻塞,但系统内核运行会很忙碌
用户进程 发起调用,没有数据立刻返回,进程继续执行, 内核会一直等待数据,当内核收到数据,会将数据复制到用户态并直接返回给进程
阻塞与非阻塞区别
阻塞,全程阻塞
非阻塞,只在数据从内核态到用户态那一段时间内阻塞
同步IO与异步IO
同步IO: 只要有阻塞的就是同步IO
异步IO: 不带一丝阻塞的就是异步IO
标签:io
原文地址:http://blog.51cto.com/xiong51/2074028