阻塞IO:只会有一次系统调用,是同步操作,在执行的进程,由于期待某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未达到或无新工作等,则由系统自动执行阻塞原语,是自己运行状态变为阻塞状态。进程的阻塞进程自身的一种主动行为,也因此只有处于运行太的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的。
非阻塞IO:会有多次系统调用。系统会定期反复去查看数据是否已经过来,如果没有会立即返回,不会滞留,如果有的话会等待数据接收,接收的过程是一个阻塞的过程。当没有接收到数据就会返回,用户进程会定期系统调发起read操作向kernel询问数据是否准备好,如果数据没准备好不会block不需要等待但是kernel会返回一个error,用户进程判断结果error就知道数据没准备好,于是再次发送read操作,反复折回,直到拿到数据,将马上将数据拷贝到用户内存,不接收数据的时候不阻塞,接收的时候阻塞,
1.如果没拿到数据,会引起多次系统调用,消耗资源
2.数据可能不能及时处理,在定期到之前,这样会滞后,数据不能及时被处理
IO多路复用:会有两次系统调用。一是select()发起的,一次是拿到数据后由recvfrom()发起的,通过select()函数发起系统调用,问kernel是否有人来连接,一直监听在那里,一旦有人来连接,就会调用recvfrom发起系统调用将数据从内核空间拷贝到用户空间。select和recvfrom都是阻塞的
eg:模仿select监听模式
###服务端,此处是水平触发
import select,socket
sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.bind(('127.0.0.1',9094))
sk.listen(5)
while 1:
r, w, e = select.select([sk, ], [], [], 5) #r是一个数组,相当于一个生成器,里面包含stream,即使没有客户端请求连接,r也是不断变化的,因为stream在不断变化,select机制就是在不停的轮循
for i in r:
conn,addr=i.accept()
data=conn.recv(1024)
print(data.decode('utf8'))
print('hello')
print('>>>>>')
conn.close()
sk.close()
####客户端
import socket
sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.connect(('127.0.0.1',9094))
while 1:
msg=input('>>>').strip()
sk.send(msg.encode('utf8'))
data=sk.recv(1024)
print(data.decode('utf8'))
sk.close()
知识点:
r,w,e=select.select([sk,],[],)
参数分别为input输入列表,output输出列表,errput错误列表
r:输入列表
w:输出列表
e:错误列表
触发方式:select是水平触发
1.水平触发 :0,1触发
2.边缘触发 :只有电平发生变化时才出发
IO多路复用优势:select全程阻塞,好处是同时可以监听多个连接,连接越多越优势明显