IO 多路复用

普通情况下,一个进程只能监视一个文件描述符(阻塞),如果使用非阻塞 IO,则会使 CPU 频繁陷入内核和空转,降低效率。而IO 多路复用是操作系统提供的接口,他会帮你同时监视多个 fd,当fd没有事件发生,调用这个接口的用户进程会阻塞,当有事件发生时,返回事件发生的 fd。这样就实现了一个进程处理多个请求。那 IO 多路复用是如何实现的?

前置知识

等待队列

在Linux内核中等待队列有很多用途,可用于中断处理、进程同步及定时。我们在这里只说,进程经常必须等待某些事件的发生。等待队列实现了在事件上的条件等待:希望等待特定事件(在本文中就是 fd 的可读可写事件)的进程把自己放进合适的等待队列,并放弃控制权(挂起)。因此,等待队列表示一组睡眠的进程,当某一条件为真时,由内核唤醒它们。

Linux 的 wakeup&callback 机制

linux(2.6+)内核的事件wakeup callback机制,这是IO多路复用机制存在的本质。Linux 通过等待队列来管理所有等待socket 某个事件的进程。等待的进程会被阻塞在等待队列上,当事件发生时,内核会遍历这个队列,检查如果某个节点进程关心这个事件,则调用这个进程节点的 call back 函数

select/poll

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

  1. 每次调用 select,用户需要传入所有关心的 socket ,CPU 陷入内核,操作系统需要将文件描述符拷贝到内核空间
  2. 内核依次检查每个 socket:把 current 轮流挂到各个 fd 的设备等待(fd)队列上,在fd有事件发生,会唤醒等待队列上的进程,此时 current 被唤醒,当进程唤醒后,将就绪事件结果保存在fds的res_in、res_out、res_ex。这个过程的时间是 O(n)。
  3. 最后检查所有 fds 的res_in、res_out、res_ex,将有事件发生的 fd 拷贝到用户传入的 fb_set 结构体数组,用户遍历找到可读文件描述符。为了效率,操作系统限制拷贝的 BitsMap 大小默认为 1024。

poll 用动态数组取代 BitsMap 但本质不变,对于每个文件描述符的检查复杂度都是 O(n)。

epoll

epoll 有 3 个接口:

  1. epoll_creat创建一个 epoll 描述符

  2. epoll_ctl动态更新关心的文件描述符

  3. epoll_wait 等待关心的 socket 事件发生

  4. 首先epoll 和 poll 不一样点是 用户不是在调用 epoll_wait 的时候才传入 fd,而是 epoll_ctl 的时候就已经传入,内核负责保存 fd,这样就省掉了不必要的重复拷贝。epoll_ctl 的同时会把 current 挂在对应 fd 的等待队列并设置回调函数。

  5. 每次调用poll系统调用,操作系统都要把current(当前进程)挂到fd对应的所有设备的等待队列上,可以想象,fd多到上千的时候,这样挂法很费事;而每次调用epoll_wait则没有这么啰嗦,epoll只在epoll_ctl时把current挂一遍(这第一遍是免不了的)并给每个fd一个命令:“好了就调回调函数”,如果设备有事件了,通过回调函数,去epoll 的红黑树查找对应节点,并将节点放在 rdllist 双向链表中。而每次调用epoll_wait就只是收集rdllist里的fd就可以了——epoll巧妙的利用回调函数,实现了更高效的事件驱动模型。

  6. epoll 通过 Linux 的回调机制,socket事件发生时Linux 内核会通过回调函数去epoll 的红黑树查找对应节点,并将节点放在 ready_list 双向链表中。最后返回这个双向链表,用户只需遍历这个链表即可得到所有就绪的socket。

边缘触发ET和水平触发LT

  • 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;
  • 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;

一般来说,边缘触发的效率比水平触发的效率要高,因为边缘触发可以减少 epoll_wait 的系统调用次数,系统调用也是有一定的开销的的,毕竟也存在上下文的切换

参考:

9.2 I/O 多路复用:select/poll/epoll | 小林coding (xiaolincoding.com)

https://www.xiaolincoding.com/os/8_network_system/selete_poll_epoll.html

Linux内核select源码剖析 | PandaDemo

poll&&epoll实现分析(一)——poll实现-lvyilong316-ChinaUnix博客

poll&&epoll实现分析(二)——epoll实现-lvyilong316-ChinaUnix博客

大话 Select、Poll、Epoll-腾讯云开发者社区-腾讯云 (tencent.com)

Linux内核——等待队列浅谈 | Shunlqing's Blog

IO 多路复用原理的更多相关文章

  1. IO多路复用原理

    (1)IO multiplexing(2)用在什么地方?多路非阻塞式IO.(3)select和poll(4)外部阻塞式,内部非阻塞式自动轮询多路阻塞式IO IO多路复用原理:其实就是整个函数对外表现为 ...

  2. IO多路复用原理&场景

    目录 IO多路复用的历史 阻塞 IO 非阻塞 IO IO 多路复用 select poll epoll IO多路复用高效的原因 IO多路复用解决的什么问题 epoll比selector性能一定更好吗 ...

  3. 理论铺垫:阻塞IO、非阻塞IO、IO多路复用/事件驱动IO(单线程高并发原理)、异步IO

    完全来自:http://www.cnblogs.com/alex3714/articles/5876749.html 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同 ...

  4. socket_server源码剖析、python作用域、IO多路复用

    本节内容: 课前准备知识: 函数嵌套函数的使用方法: 我们在使用函数嵌套函数的时候,是学习装饰器的时候,出现过,由一个函数返回值是一个函数体情况. 我们在使用函数嵌套函数的时候,最好也这么写. def ...

  5. python之IO多路复用

    在python的网络编程里,socetserver是个重要的内置模块,其在内部其实就是利用了I/O多路复用.多线程和多进程技术,实现了并发通信.与多进程和多线程相比,I/O多路复用的系统开销小,系统不 ...

  6. IO多路复用的几种实现机制的分析

    http://blog.csdn.net/zhang_shuai_2011/article/details/7675797 select,poll,epoll都是IO多路复用的机制.所谓I/O多路复用 ...

  7. 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

    下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...

  8. I/O模型系列之五:IO多路复用 select、poll、epoll

    IO多路复用之select.poll.epoll IO多路复用:通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. 应用:适用于针 ...

  9. IO多路复用和local概念

    一.local 在多个线程之间使用threading.local对象,可以实现多个线程之间的数据隔离 import time import random from threading import T ...

  10. 异步、非阻塞和IO多路复用总结

    Nginx是并发处理框架的代表者,很多后台业务都会放在Nginx容器中运行,以实现高吞吐,而Nginx能够支持高并发也是由于使用了异步非阻塞处理模型,本文将用通俗的话讲解异步.同步.阻塞.非阻塞的区别 ...

随机推荐

  1. ehlib组件包当中TDBLookupComboboxEh的小结

    TDBLookupComboboxEh和TDBGridEh一样强大无比,可以做出Combobox下拉出Grid的效果.下面是一些重要属性的小结(可怜费了我半天功夫,文档太少了.......)(1)Li ...

  2. MySQL百万级数据大分页查询优化的实现

    前言:在数据库开发过程中我们经常会使用分页,核心技术是使用用limit start, count分页语句进行数据的读取. 一.MySQL分页起点越大查询速度越慢 直接用limit start, cou ...

  3. [Java][并发编程]AQS以及其相关同步器的源码解析

    AQS以及其相关同步器的源码解析 概念 AQS(AbstractQueuedSynchronizer)抽象的队列同步器.是用来构建锁或者其他同步器组件的重量级基础框架以及整个JUC体系的基石.通过内置 ...

  4. IIS创建和管理虚拟网站

    实验介绍: 本文会详细介绍创建虚拟站点的三种方法 一:IP地址建立站点 1.打开安装了IIS的windows,进入ip配置页面. 添加几个ip,我这里添加的是192.168.1.209,192.168 ...

  5. 写好C#代码的技巧

    写好C#代码的技巧 编者导语 本文来自https://www.pluralsight.com,作者Afzaal Ahmad Zeeshan. 原文包含以下三篇文章: <编写更好的C#代码简介&g ...

  6. mac os 解决Error: EMFILE: too many open files错误

    壹 ❀ 引 构建项目时终端反复出现Error: EMFILE: too many open files的错误,经排查是因为项目较大,发布过程中已经超过了mac默认的文件监听上限,错误如下: 解决方式也 ...

  7. Springboot实现remember-me记住我功能

    1.什么是remeber-me? remeber-me即记住我功能,是我们在登录web系统时的常见勾选项.当我们登录一个web系统时除了输入常规的用户名.密码后还可以勾选记住我选项(假设该系统提供了该 ...

  8. Python之正则表达式匹配电话号码和邮箱

    代码 #! python3 # phoneAndEmail.py - Finds phone numbers and email addresses on Clipboard import pyper ...

  9. B - Bracket Sequence题解

    B - Bracket Sequence 思路: 用一个flag来标记括号的数目,如果括号数目是个偶数的话,就代表当前要执行'+'操作,反之就是'*'操作.对于最外层的数,是没有计算的. 所以最后要单 ...

  10. Basic语言开发笔记:Basic语言介绍、环境搭建、基本语法示例与程序实例

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...