zoukankan      html  css  js  c++  java
  • io模型发展史

    1.前提理论

    这里研究的io模型都是针对网络io的 , 因为现在开发的应用程序基本上都是基于网络的

    """
        Stevens在文章中一共比较了五种IO Model:
        * blocking IO           阻塞IO
        * nonblocking IO      非阻塞IO
        * IO multiplexing      IO多路复用
        * signal driven IO     信号驱动IO
        * asynchronous IO    异步IO
        由signal driven IO(信号驱动IO)在实际中并不常用,所以主要介绍其余四种IO Model。
    """
    
    #1)等待数据准备 (Waiting for the data to be ready)
    #2)将数据从内核拷贝到进程中(Copying the data from the kernel to the process)
    
    同步异步
    阻塞非阻塞
    常见的网络阻塞状态 :
        accept
        recv
        recvfrom
        
    send是主动触发的 , send的量很小 , 但是在内存copy到内存很快 
    send虽然它也有io行为但是不在我们的考虑范围
    

    image-20211017144540647

    图解recv和send

    image-20211017144940085

    image-20211017145114033

    2.阻塞IO(blocking IO)

    我们之前写的都是阻塞io , 协程除外

    在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:

    image-20211017145810299

    当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。

    而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

    所以,blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。

    回顾tcp服务端

    import socket
    
    sock = socket.socket()
    sock.bind(("127.0.0.1", 8081))
    sock.listen(1)
    print('等待客户端连接.....')
    conn, addr = sock.accept()
    while 1:
        msg = conn.recv(1024).decode()
        print(msg)
        if msg == "n": break
        conn.send(msg.upper().encode())
    conn.close()
    sock.close()
    
    # 在服务端开设多进程或者多线程 进程池线程池 其实还是没有解决工问题
    该等的地方还是得等没有规避
    只不过多个人等待的彼此互不干扰
    

    3.非阻塞IO(non-blocking IO)

    Linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:

    image-20211017150536115

    从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是用户就可以在本次到下次再发起read询问的时间间隔内做其他事情,或者直接再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存(这一阶段仍然是阻塞的),然后返回。

    也就是说非阻塞的recvform系统调用调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个error。进程在返回之后,可以干点别的事情,然后再发起recvform系统调用。重复上面的过程,循环往复的进行recvform系统调用。这个过程通常被称之为轮询。轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态。

    代码实现

    #服务端
    from socket import *
    import time
    s=socket(AF_INET,SOCK_STREAM)
    s.bind(('127.0.0.1',8080))
    s.listen(5)
    s.setblocking(False) #设置socket的接口为非阻塞
    conn_l=[]
    del_l=[]
    while True:
        try:
            conn,addr=s.accept()
            conn_l.append(conn)
        except BlockingIOError:
            print(conn_l)
            for conn in conn_l:
                try:
                    data=conn.recv(1024)
                    if not data:
                        del_l.append(conn)
                        continue
                    conn.send(data.upper())
                except BlockingIOError:
                    pass
                except ConnectionResetError:
                    del_l.append(conn)
    
            for conn in del_l:
                conn_l.remove(conn)
                conn.close()
            del_l=[]
    
    #客户端
    from socket import *
    c=socket(AF_INET,SOCK_STREAM)
    c.connect(('127.0.0.1',8080))
    
    while True:
        msg=input('>>: ')
        if not msg:continue
        c.send(msg.encode('utf-8'))
        data=c.recv(1024)
        print(data.decode('utf-8'))
    
    非阻塞IO实例
    

    总结 :

    """
    虽然非阻塞Io给你的感觉非常的牛逼
    但是该模型会长时间占用CPU  并且不干活(一直在循环)   让CPU不停的空车
    我们实际应用中也不会考虑使用非阻塞io模型
    
    任何的技术点都有它存在的意
    实际应用或者是思想借鉴
    """
    

    4.IO多路复用(IO multiplexing)

    image-20211017152744728

    当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

    这个图和blocking IO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom),而blocking IO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

    """
    当监管的对象只有一个的时候其实io多路复用连阻塞io都比比不上!!
    但是io多路复用可以一次性监管很多个对象
    
    监管机制是操作系统本身就有的如果你想要用该监管机制( select )
    需要你导入对应的select模块
    
    server = socket.socket()
    conn , addr = server.accept()
    """
    
    #服务端
    from socket import *
    import select
    
    s=socket(AF_INET,SOCK_STREAM)
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(('127.0.0.1',8081))
    s.listen(5)
    s.setblocking(False) #设置socket的接口为非阻塞
    read_l=[s,]
    while True:
        r_l,w_l,x_l=select.select(read_l,[],[])
        print(r_l)
        for ready_obj in r_l:
            if ready_obj == s:
                conn,addr=ready_obj.accept() #此时的ready_obj等于s
                read_l.append(conn)
            else:
                try:
                    data=ready_obj.recv(1024) #此时的ready_obj等于conn
                    if not data:
                        ready_obj.close()
                        read_l.remove(ready_obj)
                        continue
                    ready_obj.send(data.upper())
                except ConnectionResetError:
                    ready_obj.close()
                    read_l.remove(ready_obj)
    
    #客户端
    from socket import *
    c=socket(AF_INET,SOCK_STREAM)
    c.connect(('127.0.0.1',8081))
    
    while True:
        msg=input('>>: ')
        if not msg:continue
        c.send(msg.encode('utf-8'))
        data=c.recv(1024)
        print(data.decode('utf-8'))
    
     select网络IO模型
    

    总结

    """
    监管机制其实有很多
    select机制        windows   linux都有
    
    poll机制              只在1inux有               poll 和 select都可以监管多个对象    但是po11监管的数量更多
    
    上述 select和poll机制其实都不是很完美当监管的对象特别多的时候
    可能会出现极其大的延时响应
    
    epo11机制        只在1inux有
        它给每一个监管对象都绑定一个回调机制
        一旦有响应回调机制立刻发起提醒
        
    针对不同的操作系统还需要考虑不同检測机制书写代码太多繁琐
    有一个人能够根据你跑的平台的不同自动帮你选择对应的监管机制
    selectors模块
    """
    

    5.异步IO

    Linux下的asynchronous IO其实用得不多,从内核2.6版本才开始引入。先看一下它的流程:

    image-20211017154717046

    用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

    """
    异步io模型是所有模型中效率最高的也是使用最广泛的  感觉上面三个是一个理论推导 , 思想迭代的过程 , 最后到了这个异步io , 由于异步io是让操作系统帮我们做一些操作 , 我们通过原生的python代码是无法实现的 需要用一门能够直接和操作系统打交道的语言(c语言)处理 , 好在有人帮你封装好了模块和框架
    
    相关的模块和框架
    模块: async模块
    异步框架: sanic tronado twisted  (他们之所以快是因为内部使用的是异步io模型)
       速度快! ! ! , 快到甚至可以开发游戏服务端
    """
    

    async模块在爬虫领域中经常使用 , 后面会详细介绍怎么使用

    6.IO模型比较

    image-20211017160057398

    经过上面的介绍,会发现non-blocking IO和asynchronous IO的区别还是很明显的。在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。异步io nb

  • 相关阅读:
    [转载]解析.Net框架下的XML编程技术
    Intercept and Manage Windows Originated by Thirdparty Components Hosted in C# Application
    [创业]<高效能人士的七个习惯>有感
    [关于DES程序]对前DES1.0源码bug的修正
    The secret benefit of search engine optimisation: Increased usability
    效率不高的7个原因
    [PDF]Intrusion Detection Techniques and Approaches
    [转载]基于数据挖掘的入侵检测系统
    [转载]在 C# 中加载自己编写的动态链接库
    [转载]在WinForm中使用Web Services 来实现 软件 自动升级( Auto Update ) (C#)
  • 原文地址:https://www.cnblogs.com/xcymn/p/15721381.html
Copyright © 2011-2022 走看看