• 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的占用率

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

  • 相关阅读:
    vim配置
    git rebase
    mongodb的docker-compose.yml
    Nginx配置BrowserRouter跟随react-router
    Flux Architecture & Redux Data Flow & redux & react-redux PPT
    from acwing 从算法数量推算算法复杂度
    evalRPN 逆波兰算术
    二分区间
    Flex布局
    Treap 模板
  • 原文地址:https://www.cnblogs.com/bruce123/p/11184447.html
Copyright © 2020-2023  润新知