Day 36 IO模型

IO模型

模型即解决摸个问题的固定套路

IO模型就是指解决IO问题的固定套路

IO就是指输入输出

IO的问题就是当我们要输入数据或者输出数据通常需要很长一段时间(对于CPU而言),在等待输入的过程中,CPU处于闲置状态,早证了资源浪费

注意:IO其实有很多类型,例如socket网络IO,内存到内存的copy,等待键盘输入,对比起来socket网络IO需要等待的时间紧是最长的

网络IO经历的步骤和过程

操作系统有两种状态:内核态和用户态,当操作系统需要控制硬件时,必须先装到内核态,然后把数据从操作系统缓冲区转到内存缓冲区,冲内核态转到用户态

设计到的步骤:

  1. wait_data

  2. copy_data

    recv accpect 需要经历wait->copy

    send 只需要经历copy

阻塞IO模型

默认情况下,我们写出的TCP程序就是阻塞IO模型

改模型提高效率方式:当你执行resv/accept时,会进入wait_data阶段

  1. 进程会主动调用一个block指令,进程进入阻塞状态,同时让出CPU的执行权限,操作系统会将CPU分配给其他的任务,从而提高了CPU的利用率
  2. 当数据到达时,首先会从内核将数据copy到应用程序缓冲区,并且socket将唤醒处于自生的等待队列中的所有进程

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

非阻塞IO模型

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

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

该模型在没有数据到达时,会抛出异常,我们需要捕获异常,然后继续不断的询问系统内核知道数据到达为止,可以看出,该模型会大量的占用CPU资源,做一些无效的循环,效率也可以但是CPU占用太高了,效率低于阻塞IO模型

多路复用IO模型

属于事件驱动模型

多个socket使用同一套处理方案处理逻辑

如果将非阻塞IO比喻是点餐的话,相当于你每次去点餐台按照菜单挨个问个遍;为多路复用就是直接问那些菜做好了(批量问),点餐台会返回给你已给列表,里面就是已经做好的菜,然后挨个吃掉(挨个处理)

在多路服用中,阻塞与非阻塞没有去区别,因为select会阻塞知道有数据到

对比阻塞或非阻塞模型,增加了一个select来帮我们检测socket状态,从而避免了我们自己检测socket带来的开销问题

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

import socket,time,select

s=socket.socket()
s.bind(('127.0.0.1',8000))
s.listen(5)

# 待检测是否可读的列表
r_list=[s]
# 待检测是否可写的列表
w_list=[]
# 待发送的数据
msgs={}

print('start check')
while True:
    read_ables,write_ables,_=select.select(r_list,w_list,[])
    print('finsh check')

    # 处理可读列表,也就是接收数据
    # 拿出所有可以读数据的socket
    for obj in read_ables:
        # 有可能是服务器的,也有可能是服务端的
        # obj是服务端的
        if obj==s:
            print('find a client')
            client,addr=s.accept()
            # 新的客户端也交给select检测
            r_list.append(client)
        # obj是客户端,并且要执行recv接收数据
        else:
            print('a msg from client')
            try:
                data=obj.recv(1024).decode('utf-8')
                if not data:raise ConnectionResetError
                print(f'client msg:{data}')
                # 将要发送数据的socket加入到列表中让select检测
                w_list.append(obj)
                # 由于容器是一个列表,所以要先判断是否已经存在了列表
                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是发起任务后就可以继续执行其他任务,只有当数据已经拷贝到应用程序缓冲区才会给你的线程发一个通知,或者执行回调

信号驱动IO模型

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

posted @ 2019-07-09 15:41  萨萌萌  阅读(163)  评论(0编辑  收藏  举报