I/O多路复用-EPOLL探索
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探索的更多相关文章
- 非阻塞套接字编程, IO多路复用(epoll)
非阻塞套接字编程: server端 import socket server = socket.socket() server.setblocking(False) server.bind(('', ...
- I/O多路复用——epoll函数
1 select的低效率 select/poll函数效率比较低,主要有以下两个原因: (1)调用select函数后需要对所有文件描述符进行循环查找 (2)每次调用select函数时都需要向该函数传递监 ...
- IO多路复用epoll
0 why: 问题来源 0.1 网络编程流程 //创建socket int s = socket(AF_INET, SOCK_STREAM, 0); //绑定IP地址和端口号port bind(s, ...
- IO多路复用之epoll总结
1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...
- 多路复用(select、epoll)实现tcp服务
-------------------------------多路复用的服务器(select)------------------------------- 网络通信被Unix系统抽象为文件的读写,通 ...
- unix网络编程——I/O多路复用之epoll
1. 基本概念 当程序进行IO时,如果数据尚未准备好,那么IO将处于阻塞状态.当某个进程有多个打开的文件,比如socket,那么其后的所有准备好读写的文件将受到阻塞的影响而不能操作.不借助线程,单一进 ...
- IO多路复用之epoll
1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...
- 【python】-- IO多路复用(select、poll、epoll)介绍及实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- IO多路复用(select、poll、epoll)介绍及select、epoll的实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
随机推荐
- golang io中io.go解读
目录 1. 整体大纲 2. 接口 读 写 关闭 寻址 3. 函数 读 写 复制 4. 结构体 SectionReader LimitedReader teeReader 5. 备注 根据golang ...
- 自己动手搭建经典的3层 Asp.Net MVC
1:IBaseDAL using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expr ...
- 1-HTTPS之SNI介绍
原文:https://blog.51cto.com/zengestudy/2170245 介绍 早期的SSLv2根据经典的公钥基础设施PKI(Public Key Infrastructure)设计, ...
- mac中使用expect脚本,让iTerms保存密码登录ssh
最近工作中需要使用ssh连接到centos服务器中,以前公司都是直接配的私钥就可以免密登录了.这里还用的密码. 由于,我一直用的是iTerm2,所以在网上搜索了下,找到了一种方案,那就是expect脚 ...
- spark2.4.2 源码编译
基于Maven的构建是Apache Spark的参考构建.使用Maven构建Spark需要Maven 3.5.4和Java 8.请注意,从Spark 2.2.0开始,对Java 7的支持已被删除. 包 ...
- svn导一份历史版本出来
然后: 点击最上面的Show log ; 选择你要的版本: 右键, export, 然后就可以选择需要导出的文件夹 主要有时候开发新功能然后覆盖到项目里面去了,但是产品可能需要在旧版本上面修改其他东西 ...
- NCcat学习使用
一.使用手册 1.简介:nc/NetCat是一款端口监听工具,可以用来建立系统之间的连接.传输文件.TCP代理等. 2.命令参数 nc [-options] hostname port[s] ...
- Rust中的Cargo工作空间实践
这是为了开发大型程序,分治crate用的. 目录结构如下: 一,根cargo.toml内容 [workspace] members = [ "adder", "add-o ...
- shell脚本特殊符号——单引号、双引号、反引号、反斜杠
1. 单引号 ( '' ) 被单引号用括住的内容,将被视为单一字串. 2. 双引号 ( " " ) 被双引号用括住的内容,将被视为单一字串.双引号只要求忽略大多数,具体说,括在双 ...
- Virtualbox 修改硬盘的序列号等信息 例
Virtualbox 修改硬盘的序列号等信息 例 http://blog.csdn.net/eidolon8/article/details/42709365 原创 2015年01月14日 14:24 ...