• Python IO模型


    之前学到gevent 遇到IO操作,自动切换

    对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个阶段:

    • 1 等待数据准备 (Waiting for the data to be ready)
    • 2 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

    记住这两点很重要,因为这些IO Model的区别就是在两个阶段上各有不同的情况。

    IO模型

    • 阻塞IO
    • 非阻塞IO
    • IO多路复用(监听多个连接)
    • 异步IO
    • 驱动信号

    1 阻塞IO blocking

    默认的socket是阻塞的,任务是串行执行的

    一个典型的读操作流程:
    wait data和copy data的过程中都是在等待的,也就是阻塞的。

    内核态--用户态

    操作系统操作的
    数据存放在内核空间(数据缓冲区),操作系统把内核空间中的数据传到用户空间

    数据准备阶段

    accept发起系统调用,用户态转换成内核态
    等待数据wait for data、copy data from kernal to user
    都是阻塞,就是全程阻塞,在没有到达用户态的时候,都是阻塞

    2 非阻塞IO nonblocking

    可以通过设置socket的编程unblocking,sock.setblocking(True)

    sock.setblocking(False) 这个没有连接,立刻报错

    会立即返回是否已经连接了

    wait data 不是阻塞的
    从内核态拷贝数据 copy data到用户态的时候是阻塞的

    • 优点:不用等待数据 ,在发起系统调用的过程中是自己的时间
    • 缺点:一直发起系统调用,拿到的数据不是实时的

    3 IO多路复用 select

    IO 多路复用的本质就是非阻塞IO,它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程

    面试中的比较多

    把等待数据的过程分成了两部分:

    • select 替代看wait for data return readable代表收到数据
    • recv已经获得了数据

    select 可以连接多个套接字对象,

    r,w,e = select.select([socket,],[],[])

    socketserver是threading和select共同实现的,利用了多线程和IO多路复用

    select是在监听,监听的是有变化的套机子

    第一次监听的是sock对象,第二次连接的是conn

    socket是一个文件描述符,fd是一个整数

    select是基于IO操作,节省时间

    基于IO多路复用的socket 多并发聊天

    客户端select机制

    import socket
    import select
    import time
    
    sock = socket.socket()
    sock.bind(("127.0.0.1",8810))
    
    sock.listen(5)
    
    sock.setblocking(False)
    
    inputs = [sock,]  # 监听的列表
    while 1:
        r,w,e = select.select(inputs,[],[])  # 监听有变化的套接字,inputs  = [socket,conn1,conn2...]
        for obj in r:
            if obj==sock:  # 第一次用[socket],第二次[conn]
                conn,addr = obj.accept()
                print("conn",conn)
                inputs.append(conn) #
            else:
                data = obj.recv(1024)
                print(data.decode("utf-8"))
                send_data = input(">>>")
                obj.send(send_data.encode("utf-8"))
    

    客户端

    import socket
    
    sock = socket.socket()
    
    sock.connect(("127.0.0.1", 8810))
    
    while 1:
        data = input(">>>")
        sock.send(data.encode("utf-8"))
        recv_data = sock.recv(1024)
        print(recv_data.decode("utf-8"))
    sock.close()
    
    

    测试结果:

    有两个通道,socket是建立连接的,conn是进行通信的管道。sock对象是不变的。

    关于文件描述符(套接字对象):

    • 是非零整数,不会变(fd=xxx,xxx是一个整数,filedescription)
    • 收发数据的时候,对于接收端而言,数据先到内核空间,然后copy到用户空间,同时内核空间的数据清空(内核空间和数据空间都是内存中分配的)
    • 发送端的时候,TCP的三次握手没有应答,数据不清空

    IO多路复用总结

    IO多路复用的特性:(监听多个连接)

    • 全程阻塞 wait for data copy data
    • 监听多个文件描述符

    4 异步IO

    全程无阻塞
    实现起来复杂的

    最后总结:

    阻塞IO,进程一直等待
    非阻塞IO,copy data 的过程是阻塞的
    IO多路复用,全程阻塞

    • 有阻塞的就是同步IO
      (阻塞IO 、非阻塞IO(copy data),IO多路复用)

    • 没有阻塞的是异步IO

    [^1]http://anjianshi.net/post/yan-jiu-bi-ji/python-blocking

    [^2]http://www.cnblogs.com/yuanchenqi/articles/6755717.html

  • 相关阅读:
    Java-Class-@I:org.springframework.web.bind.annotation.RequestBody.java
    Java-Class-@I:org.springframework.validation.annotation.Validated.java
    Java-Class-@I:org.springframework.beans.factory.annotation.Autowired.java
    Java-Class-@I:org.springframework.stereotype.Service.java
    Murano环境搭建、使用介绍和思考
    简洁经常使用权限系统的设计与实现(一):构造权限菜单树的N(N>=4)种方法
    Android 依赖注入: Dagger 2 实例解说(一)
    mybatis的#和$的差别
    国内外优秀呼叫中心系统简单介绍
    openWRT学习之LUCI之中的一个helloworld演示样例
  • 原文地址:https://www.cnblogs.com/Python666/p/6835885.html
Copyright © 2020-2023  润新知