透视I/O多路复用

我写的不是select这些函数的教学,需要了解的请自行Google或者去man,这些是帮助我理解函数的封装之下的道理。

需要回答的问题

  1. I/O准备好了指什么?什么叫I/O已经可读/写?

  2. 内核如何开始监视?监视的模式如何?

  3. 内核以何种机制通知进程?

  4. 内核/用户态搬运数据的效率?

  5. 阻塞非阻塞?

什么是I/O多路复用

I/O多路复用(multiplexing)是一种单进程,单线程监视多个文件描述符(file descriptor)的软件机制。和多进程/多线程的服务方式不同,I/O多路复用将仅有的单进程阻塞起来,有一个关注的描述符集合,内核在检测到集合中有准备好读写的描述符后唤醒进程,进行I/O操作。

现在首先回答第一个问题——怎么叫做I/O准备好了

Linux文档中是这么定义的:

A file descriptor is considered ready if it is possible to perform a corresponding I/O operation (e.g., read(2) without blocking, or a sufficiently small write(2)).

此外,readfread是默认的“blocking calls”,也就是说当没有可读的数据时进程将转为blocked阻塞状态直到有数据读。

在网络编程中,一个socket(Unix里,socket无差别地是文件描述符)可读的条件是:

  1. socket内核接收缓存区中的字节数大于或等于其低水位标记SO_RCVLOWAT。此时可以无阻塞地读该socket,并且读操作返回的字节数大于0。
  2. socket通信对方关闭连接。此时对该socket读操作将返回0。
  3. 监听socket上有新的连接请求。
  4. socket上有未处理的错误。此时我们可以使用getsockopt来读取和清除该错误。

可写的条件是:

  1. socket内核发送缓冲区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT。此时我们可以无阻塞写该socket,并且写操作返回的字节数大于0。
  2. socket写操作被关闭。对写操作被关闭的socket执行写操作将触发一个SIGPIPE信号。
  3. socket使用非阻塞connect连接成功或者失败(超时)之后。
  4. socket上有未处理的错误。此时我们可以使用getsockopt来读取和清除该错误。

SO_RCVLOWAT和SO_SNDLOWAT是每个socket的两个指标,标识了缓冲超过某个值内核才通知进程socket可读。

列举这些诸条的用意是认识到,系统内核有明确的方式和指标对I/O的状态做判断,发现I/O准备好后及时通知进程。

我们已经对进程需要何种I/O有了准确的答案,现在回到定义,进程和内核之间的协作如何开展开?这里便引出了2、3两个问题。

I/O多路复用的实现

1. select/poll的实现

select在调用时调用每一个要监视的fd的poll回调(不是那个系统调用),需要知道,每一个fd有一个队列用来存放所有监视它的进程,这个回调的作用就是给fd的这个队列,叫做struct wait_queue_head_t,加入新的监视者。这是select的准备工作,到此为止,如果路上遇到了可用的fd那么直接返回,否则进程就可以安心睡会了。

此时,如果受监视的fd上有感兴趣的事情发生(比如字符写入),fd会遍历它的struct wait_queue_head_t,唤醒这些监视它的进程——仅仅是唤醒。接下来的事情是进程来做,它重复之前的迭代查询,遍历它的参数集子,确定所有 ready的fd,收集到返回值里面去,进程并不知道是谁唤醒的它,需要再检查的。那么问题3也就得到回答——内核简单唤醒进程,再丢给进程去挨个查询就好。

看着熟悉啊,这不就是visitor模式么?

pollselect的基础上取消了最大fd数量的限制。其实,在内核文件linux/posix_types.h中有声明曰#define __FD_SETSIZE    1024,当然,可以通过重新编译内核手工突破这个限制。

2. epoll的实现

问题4是一个涉及效率的讨论。select/poll机制中,当监视的fd数量巨大时,select维护的是一个超大的数据结构,伴随fd数量的增加,成本达到了O(N)级别,此外每次调用时向内核传参数、传出内核消息需要内存拷贝又是一笔开销。epoll就是一个解决此问题的出现,适用大量fd同时监听。

顾名思义,epoll_ctl意指control,执行它是对常驻内核中的数据结构(红黑树)进行增减操作,而不是每次等待时都传进全部fd。epoll_ctl还为相应的fd注册回调函数,事件发生,内核将fd插入一张链表当中,返回给用户。

为什么I/O多路复用?

I/O多路复用适用于多个文件描述符同时监听的情况。下面基于这一情景。

这首先要聊清楚什么是阻塞不阻塞的I/O(问题5)。阻塞I/O在没有输入时会将进程阻塞,直到读到数据才返回,然而非阻塞I/O在这些不好的情况下会返回一个标识错误的码,保证了进程流畅的进行下去。

前面讲过read的默认阻塞行为,那么对于一个没有足够输入的fd,read会将进程阻塞在这一个fd上,大写的不好。

考虑把read开成非阻塞式,那么会需要一个循环来不断尝试读,也就是所谓busy waiting,大概长这个样子:

while(true)
{
non-blocking read fd1;
non-blocking read fd2;
...
}

busy waiting坏处就是长时间占据CPU还可能读不到数据。

select解决这个问题。循环地在几个fd上阻塞,注意I/O多路复用可以同时在多个fd上阻塞,既满足了多路监听,又有阻塞式的低CPU占用。代码长类似这样:

while(true)
{
select_result = select(fds);
for(auto fd : select_result) deal with fd;
}

最后,现在是时候看一下讲了这么久的“多路复用”到底是什么了。wiki上是这么解释的:

In telecommunications and computer networks, multiplexing (sometimes contracted to muxing) is a method by which multiple analog or digital signals are combined into one signal over a shared medium. The aim is to share an expensive resource.

多路复用在一个昂贵的设备上组合了多路信号,放在I/O多路复用上就是,用一个阻塞式I/O函数监听多个感兴趣的fd。

此外需要注意,I/O多路复用获得ready的fd并不代表接下来的一切读写都不会阻塞(比如读一个超过SO_RCVLOWAT=100的socket,要从大小为1000的buffer发2000的数据,自信的调阻塞I/O的话就死在这了——当然从select的角度讲大可以阻塞I/O直接上),所以该和非阻塞合作的还是要设为非阻塞!

透视I/O多路复用的更多相关文章

  1. Python(七)Socket编程、IO多路复用、SocketServer

    本章内容: Socket IO多路复用(select) SocketServer 模块(ThreadingTCPServer源码剖析) Socket socket通常也称作"套接字" ...

  2. 你真的会玩SQL吗?透视转换的艺术

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  3. Lind.DDD.RedisClient~对StackExchange.Redis调用者的封装及多路复用技术

    回到目录 两雄争霸 使用StackExchange.Redis的原因是因为它开源,免费,而对于商业化的ServiceStack.Redis,它将一步步被前者取代,开源将是一种趋势,商业化也值得被我们尊 ...

  4. IO多路复用概念性

    sellect.poll.epoll三者的区别 先来了解一下什么是进程切换 为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行,这种行为为进程的切换,任务切换 ...

  5. python学习笔记-(十四)I/O多路复用 阻塞、非阻塞、同步、异步

    1. 概念说明 1.1 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可 ...

  6. Open xml 操作Excel 透视表(Pivot table)-- 实现Excel多语言报表

    我的一个ERP项目中,客户希望使用Excel Pivot table 做分析报表. ERP 从数据库中读出数据,导出到Excel中的数据源表(统一命名为Data),刷新Pivot table! 客户还 ...

  7. EXCEL 2010学习笔记 —— 数据透视表

    今天整理一下EXCEL2010 数据透视表的课程笔记,数据透视表可以对多组数据进行统计和整理,是一种基本的数据可视化工具. 记录6个方面的总结: 1.创建数据透视表 2.更改数据透视表的汇总方式 3. ...

  8. 【CSS3进阶】酷炫的3D旋转透视

    之前学习 react+webpack ,偶然路过 webpack 官网 ,看到顶部的 LOGO ,就很感兴趣. 最近觉得自己 CSS3 过于薄弱,想着深入学习一番,遂以这个 LOGO 为切入口,好好研 ...

  9. IO多路复用之select总结

    1.基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程.IO多路复用适用如下场合: (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/ ...

随机推荐

  1. 如何配置多台mysql 服务器?

    当我们数据量增大时,数据库需要进行扩张时,或者做高可用,那么我们就需要用到同时多台mysql服务器.通过配置:可以实现,一台主mysql,负责记录数据的变化,多台从mysql,查询结果.修改文件:/w ...

  2. [HDU2037]贪心入门

    今年暑假不AC Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  3. 深入理解ajax系列第三篇——响应解码

    前面的话 我们接收到的响应主体类型可以是多种形式的,包括字符串String.ArrayBuffer对象.二进制Blob对象.JSON对象.javascirpt文件及表示XML文档的Document对象 ...

  4. Java集合学习笔记

      在Java中,我们经常听到Collections框架.Collection类以及Collections类.这三者名字相似,但是从概念上讲却是不同的.Collections框架泛指Java中用于存储 ...

  5. ACM 重建二叉树

    重建二叉树 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 题目很简单,给你一棵二叉树的后序和中序序列,求出它的前序序列(So easy!).   输入 输入有多组数 ...

  6. 《C++之那些年踩过的坑(二)》

    C++之那些年踩过的坑(二) 作者:刘俊延(Alinshans) 本系列文章针对我在写C++代码的过程中,尤其是做自己的项目时,踩过的各种坑.以此作为给自己的警惕. 今天讲一个小点,虽然小,但如果没有 ...

  7. 关于如何介绍spring框架。

    一.介绍Spring 1.Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架. 2.概念:轻量级的IOC(控制反转或者依赖注入).AOP(面向切面或者面向方面) ...

  8. 一切app源于生活 用于生活 一个利于生活的app——利生活

    现在的app软件可以说 各行各业都基本达到饱和阶段 因为智能手机时代让我们的世界缩小了十倍百倍甚至千倍,我们可以足不出户知天下可以足不出户的去购物. 既然app利于生活 那么今天我所要开发的一个app ...

  9. laravel框架中所用到的依赖注入

    用Laravel开发前前后后有2个月左右了,之前一直写Java,就像找到Java和PHP之前的共同点,用Java的某些原理去理解PHP会发现还是有很多共通之处的.Java的依赖注入已经是一个很常见的概 ...

  10. SaaS模式应用之多租户系统开发(单数据库多Schema设计)

    SaaS是Software-as-a-Service(软件即服务)的简称,这边具体的解释不介绍. 多租户的系统可以应用这种模式的思想,将思想融入到系统的设计之中. 一.多租户的系统,目前在数据库存储上 ...