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

posted @ 2017-05-10 14:43  hzxPeter  阅读(250)  评论(0编辑  收藏  举报