IO复用与DefaultSelector的使用

Python 小记 2019-02-25 1490 字 1265 浏览 点赞

前言

UXNIX系统中,I/O操作分两个阶段:

  • 第一阶段:用户进程等待内核把数据准备好
  • 第二阶段:用户进程从内核中拷贝数据

根据对两个阶段的不同处理,由此划分出五个I/O模型,分别是:

  • 阻塞式I/O
  • 非阻塞式I/O
  • I/O复用
  • 信号驱动式I/O
  • 异步I/O

五种模型的区别,可见图解UNIX的I/O模型。此文清晰明朗,极易理解。

IO多路复用

I/O复用过程中,也需要等待内核准备数据,以及内核拷贝数据。但它的优势在于:可以同时等待多个描述符。阻塞式I/O只能等待一个

select,poll,epoll都是IO多路复用的机制,实现起来又各自不同。


select函数监视的文件描述符分三类:writefds,readfds,exceptfds。调用select函数后程序会被阻塞,直到有描述符进入就绪态(有数据可读、可写,或者发生except),或者等待超时,select函数会返回。最后,通过遍历存放描述符的集合(fdset)拿到就绪的描述符,去做该做的操作。

select的优点是有良好跨平台能力,被几乎所有平台支持。缺点在于一个进程能够监视的文件描述符的数量存在最大限制,在linux上一般为1024。


区别于select使用三个位图表示三个fdset的方式,poll用一个pollfd指针实现。pollfd结构包含了要监视的event发生的event(elect是“参数-值”的传递方式)。pollfd没有数量限制,同select函数一样,需要通过遍历来获取就绪的描述符。

然而,由于同一时间处于就绪态的描述符其实很少,所以随着监视的描述符数量越多,效率也会随之下降。


epoll是2.6内核提出的,作为select和poll的增强版。epoll使用一个文件描述符管理多个描述符。

epoll解决了select和epoll的痛点:(1)epoll虽存在数量限制,但是允许量很大;(2)epoll保证每个描述符在整个过程中只拷贝一次,select和poll则会多次拷贝fd集合;(3)epoll不通过遍历fd集合这种低效率的方式获取就绪描述符。

DefaultSelector

为简化select编程的难度,Python做了DefaultSelector这个上层封装。

为有效看到IO复用优势,我先利用flask搭建一个简易web server。并且让视图函数的处理时间至少1s。

from flask import Flask
import time

app = Flask(__name__)

@app.route("/mysite/<page>/")
def get_page(page):
    time.sleep(1)  # 休眠1s
    return "<h1>Page: {}</h1>".format(page)

if __name__ == "__main__":
    app.run()

未完,待续……



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论