什么是I/O多路复用

I/O多路复用就是通过一种机制,可以监视多个描述符,一旦某个IO能够读写,通知程序进行相应的读写操作。

I/O多路复用的场合

1、当客户处理多个描述字时(通常是交互式输入和网络套接字),必须使用I/O复用

2、如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用

3、如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用

Linux 下I/O多路复用的方式

SELECT、POLL、EPOLL

对比SELECT、POLL、EPOLL

SELECT缺点

每次调用select,都需要把fd集合从用户态拷贝到内核态,开销和fd的数量成正比

每次调用select都需要在内核遍历传递进来的所有fd,开销和fd的数量成正比

select支持的文件描述符数量太少,32位1024个,64位2048个

POLL缺点

poll和select本质相同,使用链表存储文件描述符,没有最大连接数的限制

下图是select、poll、kqueue(FreeBSD平台)、epoll四种方式连接数和时间关系图

上图可以看出,随着fd数量的增大,select、poll的时间消耗非常大,kqueue和epoll基本上没有变化

EPOLL优点

1、它所支持的FD上限是最大可以打开文件的数目,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

2、IO 效率不随FD数目增加而线性下降

3、epoll不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd,而不是全部遍历。

下面是一个场景,一个服务端Server.py,多个客户端订阅,服务端不断的生成消息,客户端订阅后服务端会不断的把消息发送给客户端:

Server:

 #!/usr/bin/env python
# -*- coding:utf-8 -*- __author__ = 'Andy'
import socket, select, traceback, time
import threading
import Queue gen = Queue.Queue()
connections = {}
requests = {}
responses = {} def run():
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('127.0.0.1', 8080))
serversocket.listen(5)
serversocket.setblocking(0)
epoll = select.epoll() # 创建一个epoll对象
epoll.register(serversocket.fileno(), select.EPOLLIN) # 给新建的serversocket.fileno注册一个读event try:
count = 0
while True:
events = epoll.poll() # 激活的fileno举手
count += 1
for fileno, event in events:
if fileno == serversocket.fileno(): # 当激活的fileno是新建的,给该fileno注册一个读event
connection, address = serversocket.accept()
connection.setblocking(0)
epoll.register(connection.fileno(), select.EPOLLIN)
connections[connection.fileno()] = connection
requests[connection.fileno()] = b''
responses[connection.fileno()] = b""
print "new conn.fileno is %s" % connection.fileno()
elif event & select.EPOLLIN: # 如果fileno是读event,接收发送来消息,并修改该fileno为写event,下次循环时写数据
print "read event is happing"
requests[fileno] += connections[fileno].recv(1024)
epoll.modify(fileno, select.EPOLLOUT)
print('-' * 40 + '\n' + requests[fileno].decode()[:-2])
elif event & select.EPOLLOUT: # 如果fileno是写事件,写完后正常的为挂起
if responses[fileno]:
byteswritten = connections[fileno].send(responses[fileno])
responses[fileno] = responses[fileno][byteswritten:]
if len(responses[fileno]) == 0:
epoll.modify(fileno, select.EPOLLOUT) # 需要向订阅者一直发消息,这里发完后仍为写event
print "change event to write"
elif event & select.EPOLLHUP:
epoll.unregister(fileno)
connections[fileno].close()
del connections[fileno]
print "event is HUP ===%s" % fileno
pass
except Exception, err:
print traceback.print_exc()
finally:
epoll.unregister(serversocket.fileno())
epoll.close()
serversocket.close()
print "finally" def create_data():
count = 1
while True:
count += 1
res = "Message-%s" % count
gen.put_nowait(res) # 把消息放入队列
time.sleep(0.5) def update_message():
while True:
message = gen.get()
for res in responses: # 遍历所有的活跃用户,更新消息
responses[res] = message
time.sleep(0.05) if __name__ == "__main__":
p = threading.Thread(target=create_data) # 开个线程向队列里放数据
p1 = threading.Thread(target=update_message) # 从队列中取出数据
p.start()
p1.start()
run()
p.join()
p1.join()

Client:

 #!/usr/bin/env python
# -*- coding:utf-8 -*- __author__ = 'Andy'
import socket,time
def sim_client(name,i):
connFd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
connFd.connect(("127.0.0.1", 8080))
connFd.send("Process-%s start subscibe\n\n" % name)
while True:
try:
readData = connFd.recv(1024)
if readData:
print "*"*40 + "\n" + readData.decode()
except:
time.sleep(0.2) if __name__=="__main__":
sim_client("progressage",1)

来看一下输出结果

服务端接受订阅的消息:

客户端订阅服务端的消息

才疏学浅,目前只研究了EPOLL的水平触发,还有边缘触发需要去探索

作者:Andy
出处:http://www.cnblogs.com/onepiece-andy/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

I/O多路复用-EPOLL探索的更多相关文章

  1. 非阻塞套接字编程, IO多路复用(epoll)

    非阻塞套接字编程: server端 import socket server = socket.socket() server.setblocking(False) server.bind(('', ...

  2. I/O多路复用——epoll函数

    1 select的低效率 select/poll函数效率比较低,主要有以下两个原因: (1)调用select函数后需要对所有文件描述符进行循环查找 (2)每次调用select函数时都需要向该函数传递监 ...

  3. IO多路复用epoll

    0 why: 问题来源 0.1 网络编程流程 //创建socket int s = socket(AF_INET, SOCK_STREAM, 0); //绑定IP地址和端口号port bind(s, ...

  4. IO多路复用之epoll总结

    1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...

  5. 多路复用(select、epoll)实现tcp服务

    -------------------------------多路复用的服务器(select)------------------------------- 网络通信被Unix系统抽象为文件的读写,通 ...

  6. unix网络编程——I/O多路复用之epoll

    1. 基本概念 当程序进行IO时,如果数据尚未准备好,那么IO将处于阻塞状态.当某个进程有多个打开的文件,比如socket,那么其后的所有准备好读写的文件将受到阻塞的影响而不能操作.不借助线程,单一进 ...

  7. IO多路复用之epoll

    1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...

  8. 【python】-- IO多路复用(select、poll、epoll)介绍及实现

    IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...

  9. IO多路复用(select、poll、epoll)介绍及select、epoll的实现

    IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...

随机推荐

  1. [笔记] Git 冲突处理

    这是一篇关于 git 解冲突的笔记,没有什么干货. TortoiseGit 小乌龟 通常情况下,会比较喜欢使用小乌龟解冲突,详见:git 使用 tortoisegit 解冲突 但部分复杂的场景,反而需 ...

  2. MySql数据库中正则表达式

    命令 说明 ^ 在字符的开启处进行匹配 $ 在字符的末尾处进行匹配 . 匹配任何字符(包括回车和新行) [-.] 匹配括号内的任意单个字符 [m-n] 匹配m到n之间的任意单个字符,例如[0-9],[ ...

  3. LinuxShell——正则表达式

    LinuxShell——正则表达式 摘要:本文主要学习了Shell中的正则表达式. 简介 含义 正则表达式,也称作正规表示法,是用于描述字符排列和匹配模式的一种语法规则,它主要用于字符串的模式分割.匹 ...

  4. PHP 类/对象函数

    PHP类/对象函数是PHP核心的一部分,无需要安装就可以使用. 函数名称 描述 __autoload 尝试加载未定义的类 class_alias 为一个类创建别名 class_exists 检查类是否 ...

  5. 家电制造业中MES系统发挥的作用

    人们对MES系统在家电生产领域的应用并不陌生,这是离散型MES应用的一个重要领域. 如空调.冰箱.电视.洗衣机等生产加工中均具有批量制造.多品种小批量.单件生产的等特点,很多企业也是以以订单.合同为核 ...

  6. selenium和AutoIt工具辅助下载和上传

    上传 根据AutoIt Windows Info 所识别到的控件信息打开SciTE Script Editor编辑器,编写脚本. ;ControlFocus("title",&qu ...

  7. 7.InfluxDB-InfluxQL基础语法教程--INTO子句

    本文翻译自官网,官网地址:(https://docs.influxdata.com/influxdb/v1.7/query_language/data_exploration/) 通过INTO子句,可 ...

  8. 以太网PHY寄存器分析【转】

    转自:https://blog.csdn.net/Firefly_cjd/article/details/79825869 以太网PHY寄存器分析    1 1.以太网PHY标准寄存器分析    2 ...

  9. [网络] 在浏览器输入URL回车之后发生了什么

    目录 一  前言 二  URL解析 三  DNS域名解析 1  IP 地址 2  什么是域名解析 3  浏览器如何通过域名去查询 URL 对应的 IP 呢 4  小结 四  建立连接 1  TCP三次 ...

  10. 201871010110-李华《面向对象程序设计(java)》第十一周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...