什么是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:

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. __author__ = 'Andy'
  5. import socket, select, traceback, time
  6. import threading
  7. import Queue
  8.  
  9. gen = Queue.Queue()
  10. connections = {}
  11. requests = {}
  12. responses = {}
  13.  
  14. def run():
  15. serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  16. serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  17. serversocket.bind(('127.0.0.1', 8080))
  18. serversocket.listen(5)
  19. serversocket.setblocking(0)
  20. epoll = select.epoll() # 创建一个epoll对象
  21. epoll.register(serversocket.fileno(), select.EPOLLIN) # 给新建的serversocket.fileno注册一个读event
  22.  
  23. try:
  24. count = 0
  25. while True:
  26. events = epoll.poll() # 激活的fileno举手
  27. count += 1
  28. for fileno, event in events:
  29. if fileno == serversocket.fileno(): # 当激活的fileno是新建的,给该fileno注册一个读event
  30. connection, address = serversocket.accept()
  31. connection.setblocking(0)
  32. epoll.register(connection.fileno(), select.EPOLLIN)
  33. connections[connection.fileno()] = connection
  34. requests[connection.fileno()] = b''
  35. responses[connection.fileno()] = b""
  36. print "new conn.fileno is %s" % connection.fileno()
  37. elif event & select.EPOLLIN: # 如果fileno是读event,接收发送来消息,并修改该fileno为写event,下次循环时写数据
  38. print "read event is happing"
  39. requests[fileno] += connections[fileno].recv(1024)
  40. epoll.modify(fileno, select.EPOLLOUT)
  41. print('-' * 40 + '\n' + requests[fileno].decode()[:-2])
  42. elif event & select.EPOLLOUT: # 如果fileno是写事件,写完后正常的为挂起
  43. if responses[fileno]:
  44. byteswritten = connections[fileno].send(responses[fileno])
  45. responses[fileno] = responses[fileno][byteswritten:]
  46. if len(responses[fileno]) == 0:
  47. epoll.modify(fileno, select.EPOLLOUT) # 需要向订阅者一直发消息,这里发完后仍为写event
  48. print "change event to write"
  49. elif event & select.EPOLLHUP:
  50. epoll.unregister(fileno)
  51. connections[fileno].close()
  52. del connections[fileno]
  53. print "event is HUP ===%s" % fileno
  54. pass
  55. except Exception, err:
  56. print traceback.print_exc()
  57. finally:
  58. epoll.unregister(serversocket.fileno())
  59. epoll.close()
  60. serversocket.close()
  61. print "finally"
  62.  
  63. def create_data():
  64. count = 1
  65. while True:
  66. count += 1
  67. res = "Message-%s" % count
  68. gen.put_nowait(res) # 把消息放入队列
  69. time.sleep(0.5)
  70.  
  71. def update_message():
  72. while True:
  73. message = gen.get()
  74. for res in responses: # 遍历所有的活跃用户,更新消息
  75. responses[res] = message
  76. time.sleep(0.05)
  77.  
  78. if __name__ == "__main__":
  79. p = threading.Thread(target=create_data) # 开个线程向队列里放数据
  80. p1 = threading.Thread(target=update_message) # 从队列中取出数据
  81. p.start()
  82. p1.start()
  83. run()
  84. p.join()
  85. p1.join()

Client:

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. __author__ = 'Andy'
  5. import socket,time
  6. def sim_client(name,i):
  7. connFd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
  8. connFd.connect(("127.0.0.1", 8080))
  9. connFd.send("Process-%s start subscibe\n\n" % name)
  10. while True:
  11. try:
  12. readData = connFd.recv(1024)
  13. if readData:
  14. print "*"*40 + "\n" + readData.decode()
  15. except:
  16. time.sleep(0.2)
  17.  
  18. if __name__=="__main__":
  19. 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. C 储存类与运算符

    储存类 参考链接:https://www.runoob.com/cprogramming/c-storage-classes.html 存储类定义 C 程序中变量/函数的范围(可见性)和生命周期 au ...

  2. mac下安装php7.2、mysql5.7、nginx环境

    本篇文章是通过homebrew安装,home brew对于Mac相当于centos 的yum一样方便简单,大家可以先去安装home brew.网上很多简单靠谱的例子,这里不写了 一.准备条件为了安装最 ...

  3. 前端开发CSS3——文本样式和盒子及样式

    博主废话少说,直接介绍css常用的属性和属性值:属性和值只需过一遍,页面的结构还是需要布局,布局的只是后期会更新的. 提供一些图标的网站:font-awesome:     http://fontaw ...

  4. 拥抱小程序,WeTest小程序全链路测试解决方案正式上线

    背景 随着微信开放小程序开发功能,迅速在各个实体店抢占流量入口,广大商家看到了在线和离线的机会整合,利用小程序版本特点低成本进入市场,达到流量的获取和转化. 伴随着资本的进入,小程序开发市场也因此越来 ...

  5. 英语NanyangJade南阳玉NanyangJade独山玉

    南阳玉NanyangJade又叫做独山玉,独山玉,因产于河南南阳的独山而得名,亦称“南阳玉”. 独山玉是中国四大名玉之一,有南阳翡翠之称,独山玉是一种黝帘石化斜长岩,由多种矿物组成,属多色玉器. 据文 ...

  6. pip python

    简介 pip 是一个安装和管理 Python 包的工具,python安装包的工具有easy_install, setuptools, pip,distribute.使用这些工具都能下载并安装djang ...

  7. 修改源代码时不需要重启tomcat服务器

    我们在写JSP + Servlet 的时修改了Java代码就要重新启动服务器.十分麻烦. 为了解决这个问题我们可以将服务器改成debug 模式.就是按调试状态这样修改Java代码就不用再重新启动服务器 ...

  8. tp5中使用原生sql查询总结【转】

    注意事项: 1.先在database.php中配置好数据库 2.只要是数据库操作必须引用 use/think/Db;严格区分大小写. 下面是方法: public function hello5() { ...

  9. 获取mac地址和扫描端口

    获取IP mac地址 arp -a 10.0.0.34 扫描指定IP 1到30000端口 nmap -p1-30000 10.0.0.36

  10. [TCP/IP] TCP报文长度是由什么确定的

    MTU:最大传输单元,以太网的MTU为1500Bytes MSS:最大分解大小,为每次TCP数据包每次传输的最大数据的分段大小,由发送端通知接收端,发送大于MTU就会被分片 TCP最小数据长度为146 ...