【IO input output】
-
在内存中存在数据交换的操作都可以认为是输入输出
比如:
内存和磁盘交互 读写操作
内存和网络交互 recv send -
IO密集型程序:程序执行大量的IO操作,而较少的cpu运算
特点:消耗cpu较少,运行时间长 -
CPU密集型程序(计算密集型):程序中大量的操作都需要cpu运算,IO操作较少
特点:消耗cpu大,运行速度快 -
IO分类:
阻塞IO 非阻塞IO IO多路复用 事件IO 异步IO-
阻塞IO:
默认形态 效率很低的一种IO情形
阻塞情况:
1. 因为某种条件没有达成造成的阻塞
e.g. accept recv input
2. 处理IO事件的时候耗时比较长形成阻塞
e.g. 文件的读写过程,网络数据发送过程 -
非阻塞IO:
通过修改IO事件的属性,使其变为非阻塞的状态。(改变了第一种阻塞的状况)
通常和循环搭配使用,不断检测循环条件是否已经满足
s.setblocking()
功能:将套接字设置成非阻塞状态
参数:bool 设置为False则表示设置为非阻塞示例:
# block_server.py from socket import * from time import sleep,ctime s = socket() s.bind(('127.0.0.1',8888)) s.listen(5) # 将s设置为非阻塞 s.setblocking(False) while True: print('waiting for connect......') try: connfd,addr = s.accept() except BlockingIOError: sleep(2) print(ctime()) continue connfd,addr = s.accept() print('Connect from',addr) # connfd.setblocking(False) # 将recv变成非阻塞 while True: data = connfd.recv(1024),decode() if not data: break print(data) connfd.sendall(ctime().encode()) connfd.close() s.close()
-
-
超时检测
定义:
将原本阻塞的函数,设置一个阻塞的最长时间,
在规定的时间内,如果条件达到则正常执行,如果仍然阻塞则抛出异常
s.settimeout(sec)
功能:设置套接字超时时间
参数:设置的时间
示例:# timeout.py from socket import * from time import sleep,ctime s = socket() s.bind(('127.0.0.1',8888)) s.listen(5) # 设置s的超时时间(设置为5秒) s.settimeout(5) while True: print('waiting for connect......') try: connfd,addr = s.accept() except timeout: sleep(2) print(ctime()) continue connfd,addr = s.accept() print('Connect from',addr) # connfd.setblocking(False) # 将recv变成非阻塞 while True: data = connfd.recv(1024),decode() if not data: break print(data) connfd.sendall(ctime().encode()) connfd.close() s.close() -
IO多路复用(重难点):
原来传统的自上而下的执行过程中,一个一个来执行的,第二个要等到第一个执行完毕才可以执行
IO多路复用把他们并列,谁可以执行了就先执行,不必依照顺序来-
定义:
同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。
以此形成,多个IO事件都可以操作,不必逐个等待执行的效果
目的:当程序中有多个IO事件的时候提高IO的执行效率 -
准备就绪:
IO事件即将发生的临界状态 -
import select
select —> windows / linux / unix
poll------> linux / unix
epoll-----> linux / unix -
select函数原型:
r,w,x = select(rlist, wlist, xlist[, timeout])
功能:
监控IO事件,阻塞等待IO事件的发生
参数:
rlist:列表 存放我们监控等待处理的IO事件
wlist:列表 存放我们要主动处理的IO事件
xlist:列表 存放如果发生异常需要我们处理的IO事件
timeout:数字 超时时间
返回值:
r:列表 rlist当中准备就绪的IO
w:列表 wlist当中准备就绪的IO
x:列表 xlist当中准备就绪的IO
注意事项:
1. 在处理IO过程中不应该发生死循环(某个IO单独占有服务器)
2. IO多路复用形成了一种并发的效果,效率较高
示例:
写一个select服务端,同时关注客户端的连接,客户端的发送和终端的输入。
将客户端发送的内容和终端输入的内容均写入到一个文件中# select_server_zhongduan.py from socket import * from select import select import sys s = socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(('127.0.0.1',8888)) s.listen(5) rlist = [s,sys.stdin] wlist = [] xlist = [] f = open('select_file','w') while 1: rs,ws,xs = select(rlist,wlist,xlist) for r in rs: if r is s: # 表明有客户端连进来 connfd,addr = r.accept() # print('Connect from',addr) rlist.append(connfd) elif r is sys.stdin: # 表明从终端输入内容了 data = r.readline() f.write(data) else: # 表明有客户端发消息 data = r.recv(1024) if not data: rlist.remove(r) else: f.write(data.decode()) for w in ws: pass for x in xs: pass -
7.位运算:
按照二进制位进行操作运算
& | ^ << >>
按位与 按位或 按位异或 左移 右移
11 1011
14 1110
& 1010(一0则0)
| 1111(一1则1)
^ 0101(相同为0不同为1)
11<<2 101100(右边补0)==44
14>>2 11(挤掉低位的数字) ==3
使用:
1.在做底层硬件的寄存器操作
2.在做标志位过滤时
标志位过滤:
1 0 1 1(查有没有) 0---->22
0 0 0 0 0
----------------------------------------
(用00000按位与,如果那项按位与后不等于0则表示有,否则为0)
0 0 0 1 0----->!=0
0 0 0 0 0----->=0
-
poll的用法
-
创建poll对象
p = select.poll() -
添加关注对象
p.register(s,POLLIN | POLLERR)
括号内的格式事件分类,用按位或
p.unregister(s)
poll IO事件类型分类:
POLLIN POLLOUT POLLERR POLLHUP POLLPRI POLLVAL
rlist wlist xlist 断开 紧急处理 无效数据 -
进行监控
events = p.poll()
功能:阻塞等待reigister的事件发生
返回值:events 是一个列表,列表中每个元素表示准备就绪需要处理的IO
[(fileno,event, (),()]
描述符 具体什么就绪了
描述符地图 {s.fileno()😒} -
处理IO事件
-
示例:
# poll_server.py from socket import * from select import * # 创建套接字 s = socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(('127.0.0.1',8888)) s.listen(5) # 创建poll对象 p = poll() # 建立通过fileno查找IO对象的地图 fdmap = {s.fileno():s} # 添加关注 p.register(s,POLLIN | POLLERR) while True: # 进行监控 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) # 时刻维护地图更新 fdmap[c.fileno()] = c # 表示POLLIN准备就绪了 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('收到了'.encode())
-
-
epoll的用法:
使用方法:
代码基本与poll(见上)相同
1. 将生成对象的poll()函数变为
创建epoll对象:p = epoll()
2. 将register注册IO事件时,关注的事件类别改为epoll类别
添加关注:p.register(s,EPOLLIN | EPOLLERR)
区别:
epoll效率要高于poll和select
epoll的关注触发方式多一些
来源:CSDN
作者:雨醉东风
链接:https://blog.csdn.net/zhangxuelong461/article/details/104058872