IO模型

IO模型

模型即解决某个问题的固定套路或方法.

I/O指的是输入/输出,输入或输出数据需要很长一段时间(相对于CPU而言),在等待输入/输出的过程中,CPU处于闲置状态,造成资源浪费.

注意:IO类型较多,例如socket的网络IO,等待键盘的输入等,对比起来socket的网络IO需要等待的时间是最长的(解决IO的主要问题).

1. socket网络IO步骤和过程:

操作系统的两种状态:内核态(权限极高)和用户态,当操作系统需要控制硬件时,例如接受网卡数据,先切换到内核态

  • wait_data:等待数据到达网卡

    buffer(缓冲):数据读入内存占用的空间,降低IO次数

    cache(缓存):内存读取数据占用的空间,提高读取效率

  • copy_data:从内核copy到应用程序缓冲区

recv(),accept()需要经历wait和copy阶段,

而send()只经过copy

2.阻塞IO模型

默认情况下 你写出TCP程序就是阻塞IO模型

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

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

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

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

img

3.非阻塞IO模型

在调用recv()/accept()时不会阻塞

使用方法:1. server.setblocking(false)

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

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

img

4.多路复用IO模型

属于事件驱动模型

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

如果将非阻塞IO 比喻是点餐的话,相当于你每次去前台,照着菜单挨个问个遍

多路复用,直接为前台那些菜做好了,前台会给你返回一个列表,里面就是已经做好的菜

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

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

img

import socket
import time
import select

server = socket.socket()

# server.setblocking(False)   #在多路复用中,阻塞出现在select()

server.bind(('127.0.0.1',1111))

server.listen(5)

#待检测是否可读的列表
r_list = [server]
#待检测是否可写的列表
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
        #(服务器or客户端)
        if obj == server:   #服务器
            c,addr = server.accept()
            r_list.append(c)    #客户端交给select检测

        else:   #如果是客户端,接受数据

            try:    #处理客户端异常关闭
                data = obj.recv(1024)
                if not data:raise ConnectionResetError

                #将要发送数据的socket加入到列表中让select检测
                w_list.append(obj)

                #将socket对象及发送的data数据放到临时的字典中{socket:[data,data1]}
                if obj in msgs:
                    msgs[obj].append(data)
                else:
                    msgs[obj] = [data]
            except ConnectionResetError:
                obj.close()
                r_list.remove(obj)	#检测列表中清除socke

    #处理可写(发送响应数据)
    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.remove(obj)

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

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

原文地址:https://www.cnblogs.com/bruce123/p/11184447.html