IO多路复用

IO

IO input output
  在内存中存在数据交换的操作都可以认为是IO操作
  和终端交互 : input print
  和磁盘交互 : read write
  和网络交互 : recv send

IO密集型程序 : 在程序执行过程中存在大量IO操作,而cpu运算操作较少。消耗cpu较少,运行效率较低

计算密集型程序(cpu密集型程序):在程序执行中cpu运算较多,IO操作相对较少。消耗cpu大,运行速度快

IO分类
  阻塞IO 非阻塞IO IO多路复用


阻塞IO
  阻塞IO是IO的默认形态,是效率较低的一种IO情形。

阻塞情况
  * 因为某种条件没有达成造成的阻塞
  e.g. accept input recv
  * 处理IO数据传输时间较长形成的阻塞
  e.g. 网络传输过程,文件读写过程

非阻塞IO
  通过修改IO事件的属性,使其变为非阻塞状态。(让一些条件阻塞函数不再阻塞)

  * 非阻塞IO往往和循环判断一起使用

  s.setblocking(False)
  将套接字设置为非阻塞状态

超时检测
  将原本阻塞的函数设置一个最长阻塞时间。如果时间内条件达成则正常运行,如果仍然阻塞则视为超时,继续向下运行或产生异常

  s.settimeout(sec)
  设置套接字的超时时间

 

IO多路复用

  定义 : 同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。以此形成可用同时操作多个IO的并发行为,避免一个IO阻塞,造成所有IO都无法执行。

IO准备就绪 : 是一种IO必然要发生的临界状态

IO多路复用的编程实现
  1. 将IO设置为关注IO
  2. 将关注IO提交给内核监测
  3. 处理内核给我们反馈的准备就绪的IO

  具体方案:
    select ---》 windows linux unix
    poll --》 linux unix
    epoll --》 linux unix

 

epoll方法
  使用方法:基本与poll方法相同
  * 将生产对象 poll() 改为epoll()
  * 将所有poll对象事件改为epoll对象事件

 

 

区别 :
  epoll 的效率要比 poll和select 高
  epoll 的事件触发方式更多

 

----------------------------------------------------------------

import select

rs,ws,xs = select(rlist, wlist, xlist[, timeout])
  功能: 监控IO事件,阻塞等待IO事件发生
  参数: rlist 列表 存放我们监控等待处理的IO事件
  wlist 列表 存放我们要主动操作的IO事件
  xlist 列表 我们要关注出错处理的IO事件
  timeout 超时时间
  返回值:rs 列表 rlist中准备就绪的IO
  ws 列表 wlist中准备就绪的IO
  xs 列表 xlist中准备就绪的IO

注意 :  1. wlist中如果有IO事件则select立即回返回为ws
    2. 在处理IO过程中不要处理一个客户端长期占有服务端使服务端无法运行到select的情况
    3. IO多路复用占用计算机资源少,io效率高

 

# -*- coding: utf-8 -*-

from select import select
from socket import *

# 创建套接字作为关注的IO
s = socket()
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(('0,0,0,0', 8888))
s.listen(5)

rlist = [s]
wlist = []
xlist = [s]

while True:
    # 提交监测关注的IO等待IO发生
    rs, ws, xs = select(rlist, wlist, xlist)
    for r in rs:
        if r is s:
            c, addr = r.accept()
            print('Connect from', addr)
            rlist.append(c)  # 添加到关注列表
        else:
            data = r.recv(1024)
            if not data:
                rlist.remove(r)
                r.close()
            else:
                r.send(b'Receive your message!')
s.close()

# 一般只用rlist,其他的基本不用

 

 

 

-----------------------------------------------------------------

poll
1.创建poll对象
  p = select.poll()
2.添加注册事件
  p.register(s,POLLIN | POLLERR)

  POLLIN POLLOUT POLLERR POLLHUP POLLNVAL
  rlist wlist xlist 断开 无效数据

  p.unregister(s) 从关注事件中移除

3. 阻塞等待IO发生
  events = p.poll()
    功能 : 阻塞等待IO发生
    返回值 : events 是一个列表,列表中给每一个元素都是一个元组,代表一个发生的IO事件
    [(fileno, event),(),()....]
    就绪IO的文件描述符 具体就绪事件

    * 需要通过文件描述符(fileno)找到对应的IO对象
    {s.fileno() : s}

 

# -*- coding: utf-8 -*-
from socket import *
from select import *

# 创建套接字作为我们关注的IO
s = socket()
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(('0.0.0.0', 8888))
s.listen(5)

# 创建poll对象
p = poll()

# fileno--->IO对象的字典
fdmap = {s.fileno(): s}

# 注册关注的IO
p.register(s, POLLIN | POLLERR)

while True:
    # 进行IO监控
    events = p.poll()
    for fd, event in events:
        if fd == s.fileno():
            c, addr = fdmap[fd].accept()
            p.print('Connect from', addr)
            # 添加新的关注事件
            p.register(c, POLLIN | POLLHUP)
            fdmap[c.fileno()] = c
            elif event & POLLIN:
                data = fdmap[fd].recv(1024)
                if not data:
                    # 客户端退出,从关注事件移除
                    p.unregister(fd)
                    fdmap[fd].close()
                    del fdmap[fd]

 

posted on 2018-11-01 10:38  蓝天梦nice  阅读(212)  评论(0)    收藏  举报

导航