IO模型

IO模型

1,模型其实是解决某个问题的套路

2,IO指的是输入输出 in /out

3,IO存在的问题:对于我们在输入输出数据时,通常要花一些时间,但是对于CPu而言,这个等待的时间时非常漫长的,也就是CPU处于闲置状态,造成了资源的浪费

e.g.

​ 对比在IO类型中,有socket网络IO,内存到内存的IO,等待键盘输入

其中网络中的IO需要等待的时间是最长的

目的:学习IO模型,去解决CPU在等待IO操作带来的 资源浪费问题

网络IO经历的步骤和过程

首先操作系统有两种状态:内核态,用户态

操作系统控制硬件

接收网卡上数据---->转到内核态---->接收完数据后----->要把数据从操作系统缓冲区copy到硬用程序缓冲区,从内核态转为用户态

步骤:1.wait_data--->2.copy_data

recv accept 需要经历 wait---->copy

send 只需要经历copy

阻塞IO模型

默认情况下,tcp程序就是阻塞IO模型

该模型 提高效率方式,当执行recv/accept 会进入wait_data 阶段

1你的进程会注定调动一个block指令,进程进入阻塞状态,同时让出CPU的执行权,操作系统就会将CPU分配给其他的任务,从而提高了CPU的利用率

2当数据到达时,首先会从内核将数据copy到应用程序缓冲区,并且socket将唤醒处于自身的等待队列中的所有进程

之前使用多线程,多进程 完成的并发其实都是阻塞IO模型,每个线程在执行recv时,也会卡住

img

非阻塞IO模型

非阻塞IO模型与阻塞模型相反,在调用recv/accept时 都不会阻塞当前线程

使用方法:将原本的阻塞的socket设置为非阻塞

img

该模型在没有数据到达时,会抛出异常,我们需要捕获异常,然后继续不断地询问系统内核直到数据到达为止

可以看出,该模型会大量的占用CPU资源做一些无效的循环,效率低于阻塞IO

多路复用IO模型

多用于驱动模型

多个socket使用一套处理逻辑

对比阻塞或者非阻塞模型,增加了一个select,来帮我们检测socket的状态

,从而避免了我们自建检测socket带来的开销

select会把已经就绪的放入到列表里,我们需要遍历列表,分别处理读写即可

img

案例:

import socket
import time
import select
s = socket.socket()
s.bind(("127.0.0.1",1688))
# 设置为非阻塞 模型
s.setblocking(True) #在多路复用中  阻塞与非阻塞没有区别 因为select会阻塞直到有数据到达为止
s.listen()

# 待检测是否可读的列表
r_list = [s]
# 待检测是否可写的列表
w_list = []

# 待发送的数据
msgs = {}

print("开始检测了")
while True:
    read_ables, write_ables, _= select.select(r_list,w_list,[])
    print("检测出结果了!")
    # print(read_ables,"可以收数据了")
    # print(write_ables,"可以发数据了")
    # 处理可读 也就是接收数据的
    for obj in read_ables: # 拿出所有可以读数据的socket
        #有可能是服务器 有可能是客户端
        if s == obj: # 服务器
            print("来了一个客户端 要连接")
            client,addr = s.accept()
            r_list.append(client)  # 新的客户端也交给select检测了
            
        else:# 如果是客户端则执行recv 接收数据
            print("客户端发来一个数据")
            try:
                data = obj.recv(1024)
                if not data:raise ConnectionResetError
                print("有个客户端说:",data)
                # 将要发送数据的socket加入到列表中让select检测
                w_list.append(obj)
                # 将要发送的数据已经socket对象丢到容器中
                if obj in msgs:  # 由于容器是一个列表 所以需要先判断是否已经存在了列表
                    msgs[obj].append(data)
                else:
                    msgs[obj] = [data]
            except ConnectionResetError:
                obj.close()
                r_list.remove(obj)
    # 处理可写的 也就是send发送数据
    for obj in write_ables:
        msg_list = msgs.get(obj)
        if msg_list:
            # 遍历发送所有数据
            for m in msg_list:
                try:
                    obj.send(m.upper())
                except ConnectionResetError:
                    obj.close()
                    w_list.remove(obj)
                    break
            # 数据从容器中删除
            msgs.pop(obj)
        # 将这个socket从w_list中删除
        w_list.remove(obj)

多路复用对比非阻塞,多路复用可以极大降低CPU的占用率

注意:多路复用并不是完整的,因为本质上多个任务之间是串行的,如果某个任务耗时比较长,将导致其他的任务不能立即执行,多路复用的最大优势就是高并发

异步IO模型

非阻塞IO不等于异步IO 因为从copy的过程是一个同步任务 会卡主当前线程

而异步IO 是发起任务后 就可以继续执行其它任务,当书记以及copy到应用程序缓冲区,才会给你的线程发送信号 或者执行回调

信号驱动IO模型

见得说就是 当某个事情发生后 会给你的线程发送一个信号,你的线程就可以去处理这个任务

不常用,原因是 socket的信号太多,处理起来非常繁琐

原文地址:https://www.cnblogs.com/zhuyuanying123--/p/11158858.html