python 之I/O模型

I/O模型:

同步(synchronous)IO

异步(asynchronous)IO

阻塞(blocking)IO

非阻塞(non-blocking)IO

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

1)等待数据准备(wait   data)

2)将数据从内核拷贝到进程中(copying data)

阻塞IO

linux中,默认情况下所有的socket都是blocking,一个典型的读操作大概如下图:

          当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。
所以,blocking IO的特点就是在IO执行的两个阶段都被block了。

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

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

从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。所以,用户进程其实是需要不断的主动询问kernel数据好了没有。

 注意:

      在网络IO时候,非阻塞IO也会进行recvform系统调用,检查数据是否准备好,与阻塞IO不一样,”非阻塞将大的整片时间的阻塞分成N多的小的阻塞, 所以进程不断地有机会 ‘被’ CPU光顾”。即每次recvform系统调用之间,cpu的权限还在进程手中,这段时间是可以做其他事情的,

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

import time
import socket
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt
sk.bind((‘127.0.0.1‘,6667))
sk.listen(5)
sk.setblocking(False)
while True:
    try:
        print (‘waiting client connection .......‘)
        connection,address = sk.accept()   # 进程主动轮询
        print("+++",address)
        client_messge = connection.recv(1024)
        print(str(client_messge,‘utf8‘))
        connection.close()
    except Exception as e:
        print (e)
        time.sleep(4)

#############################client

import time
import socket
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

while True:
    sk.connect((‘127.0.0.1‘,6667))
    print("hello")
    sk.sendall(bytes("hello","utf8"))
    time.sleep(2)
    break

优点:能够在等待任务完成的时间里干其他活了(包括提交其他任务,也就是 “后台” 可以有多个任务在同时执行)。

缺点:任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。

三. IO multiplexing(IO多路复用)

  IO multiplexing这个词可能有点陌生,但是如果我说select,epoll,大概就都能明白了。有些地方也称这种IO方式为event driven IO。我们都知道,select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。它的流程如图: 

  当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
  这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。(多说一句。所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)
  在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。

注意1:select函数返回结果中如果有文件可读了,那么进程就可以通过调用accept()或recv()来让kernel将位于内核中准备到的数据copy到用户区。

注意2: select的优势在于可以处理多个连接,不适用于单个连接

#***********************server.py
import socket
import select
sk=socket.socket()
sk.bind(("127.0.0.1",8801))
sk.listen(5)
inputs=[sk,]
while True:
    r,w,e=select.select(inputs,[],[],5)
    print(len(r))

    for obj in r:
        if obj==sk:
            conn,add=obj.accept()
            print(conn)
            inputs.append(conn)
        else:
            data_byte=obj.recv(1024)
            print(str(data_byte,‘utf8‘))
            inp=input(‘回答%s号客户>>>‘%inputs.index(obj))
            obj.sendall(bytes(inp,‘utf8‘))

    print(‘>>‘,r)

#***********************client.py

import socket
sk=socket.socket()
sk.connect((‘127.0.0.1‘,8801))

while True:
    inp=input(">>>>")
    sk.sendall(bytes(inp,"utf8"))
    data=sk.recv(1024)
    print(str(data,‘utf8‘))

import selectors
import socket

sel = selectors.DefaultSelector()   #会根据操作系统自动选择一个IO多用复用模型

def accept(sock, mask):
    conn, addr = sock.accept()  # Should be ready
    print(‘accepted‘, conn, ‘from‘, addr)
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn, mask):
    data = conn.recv(1000)  # Should be ready
    if data:
        print(‘echoing‘, repr(data), ‘to‘, conn)
        conn.send(data)  # Hope it won‘t block
    else:
        print(‘closing‘, conn)
        sel.unregister(conn)
        conn.close()

sock = socket.socket()
sock.bind((‘127.0.0.1‘, 8080))
sock.listen(100)
sock.setblocking(False)     #设置非阻塞

#注册  把sock描述符和accept函数绑定
sel.register(sock, selectors.EVENT_READ, accept)

while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)

selectors

四 .Asynchronous I/O(异步IO)

  异步最大特点:全程无阻塞

  linux下的asynchronous IO其实用得很少。先看一下它的流程:

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

时间: 2024-11-05 16:32:53

python 之I/O模型的相关文章

python 生产者消费者线程模型

python 多线程生产者消费者模型: 一个生产者多个消费者 The Queue module implements multi-producer, multi-consumer queues. It is especially useful in threaded programming when information must be exchanged safely between multiple threads. The Queue class in this module imple

python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API

python  Django教程  之 模型(数据库).自定义Field.数据表更改.QuerySet API 一.Django 模型(数据库) Django 模型是与数据库相关的,与数据库相关的代码一般写在 models.py 中,Django 支持 sqlite3, MySQL, PostgreSQL等数据库,只需要在settings.py中配置即可,不用更改models.py中的代码,丰富的API极大的方便了使用. 本节的代码:(Django 1.6, Python 2.7 测试环境) 大

用 Python 理解 Web 并发模型

用 Python 理解 Web 并发模型 http://www.jianshu.com/users/1b1fde012122/latest_articles 来源:MountainKing 链接:http://www.jianshu.com/p/80feb3bf5c70# 前言 虽然异步是我们急需掌握的高阶技术,但是不积跬步无以至千里,同步技术的学习是不能省略的.今天这篇文章主要用Python来介绍Web并发模型,直观地展现同步技术的缺陷以及异步好在哪里. 最简单的并发 import socke

Python 多线程同步队列模型

Python 多线程同步队列模型 我面临的问题是有个非常慢的处理逻辑(比如分词.句法),有大量的语料,想用多线程来处理. 这一个过程可以抽象成一个叫"同步队列"的模型. 具体来讲,有一个生产者(Dispatcher)一方面从语料中读入句子,并且存入队列中,一方面看有没有空闲的消费者(Segmentor),如果有,就把句子从队列中弹出并交给这个空闲的消费者处理. 然后消费者把处理完成的结果交给生产者输出,生产者要保证输出与输入顺序一致. 消费者是典型的threading,它需要看见生成者

Python基础之 Django模型

本章节主要是包括通过Python安装Mysql驱动(mysqlclient),通过Django创建app,更新数据库模型. 1.安装 mysql 驱动.如果你没安装 mysql 驱动,可以执行以下命令安装: pip install mysqlclient 2.通过Django创建项目.可以执行以下命令安装: python django-admin.py startproject testModel 3.进入创建项目,通过Django创建app.可以执行以下命令安装: python  django

Python并发编程-事件驱动模型

 一.事件驱动模型介绍                                                                                                        1.传统的编程模式  例如:线性模式大致流程 开始--->代码块A--->代码块B--->代码块C--->代码块D--->......--->结束 每一个代码块里是完成各种各样事情的代码,但编程者知道代码块A,B,C,D...的执行顺序,唯一能

python异步编程--回调模型(selectors模块)

目录 0. 参考地址 1. 前言 2. 核心类 3. SelectSelector核心函数代码分析 3.1 注册 3.2 注销 3.3 查询 4. 别名 5. 总结 6. 代码报错问题 1. 文件描述符数量 2. 监听列表是否可以为空 7. 关系图 0. 参考地址 基本介绍 https://www.cnblogs.com/yinheyi/p/8127871.html 实验演示 https://www.cnblogs.com/xybaby/p/6406191.html#_label_2 详细讲解

(转)python异步编程--回调模型(selectors模块)

原文:https://www.cnblogs.com/zzzlw/p/9384308.html#top 目录 0. 参考地址 1. 前言 2. 核心类 3. SelectSelector核心函数代码分析 3.1 注册 3.2 注销 3.3 查询 4. 别名 5. 总结 6. 代码报错问题 1. 文件描述符数量 2. 监听列表是否可以为空 7. 关系图 0. 参考地址 基本介绍 https://www.cnblogs.com/yinheyi/p/8127871.html实验演示 https://w

python测试开发django-40.模型(model)中choices使用

前言 之前一直在想页面上如果一个字段只有固定的几个选项,类似select下拉框这种,如果在表里面设置一个外键的话,是不是有点傻了,这样为了几个选项弄一张表不值得. 后来看到Django模型中的字段有个choices属性,在设计模型的时候,只需把字段参数加个choices即可,看样子是我想多了~ choices参数 如果一个字段设置了choices属性,在模版中如果我要显示这个字段,比如用户的性别,只需设置2个参数选项. 在django模版系统就会将它默认解析为一个下拉菜单,这样对于一个静态的下拉

【Python数据挖掘】回归模型与应用

线性回归 ( Linear Regression ) 定义:在监督学习中,学习样本为 D = { (x(i), y(i)):i =1, . . . , m } ,预测的结果y(i)为连续值变量,需要学习映射 f:X → Y ,并且假定输入X和输出Y之间有线性相关关系. 给出一组数据: 其中x是实数域中的二维向量.比如,xi1是第i个房子的居住面积,xi2是这个房子的房间数. 为了执行监督学习,我们需要决定怎样在计算机中表示我们的函数/假设.我们可以近似地使用线性函数来表示. (矩阵形式) 现在,