I/O多路复用的一些知识总结

什么是IO多路复用呢?

IO多路复用的实现有哪些呢?

它们的区别是什么呢?

为了回答上面三个问题,我总结得到了这篇文章。

什么是IO多路复用呢?

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

描述符:内核(kernel)利用文件描述符(file descriptor)来访问文件。 文件描述符是非负整数。 打开现存文件或新建文件时,内核会返回一个文件描述符。 读写文件也需要使用文件描述符来指定待读写的文件。

IO多路复用是同步非阻塞方式,由于同步非阻塞方式需要不断主动轮询,轮询占据了很大一部分过程,轮询会消耗大量的CPU时间,而 “后台” 可能有多个任务在同时进行,人们就想到了循环查询多个任务的完成状态,只要有任何一个任务完成,就去处理它。如果轮询不是进程的用户态,而是有人帮忙就好了。那么这就是所谓的 “IO 多路复用”。UNIX/Linux 下的 select、poll、epoll 就是干这个的(epoll 比 poll、select 效率高,做的事情是一样的)。

其最大优势就系统开销小,系统不必要创建过多的进程/线程,减少了上下文切换的消耗和系统资源。

主要适用如下场合:

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

  • 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用;

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

  • 当客户端需要处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用。

IO多路复用的实现有哪些呢?

在内核中的实现有 select、poll、epoll ,其出现顺序也是如此。

select

用户线程会启动一个监视Socket调用select函数以后会阻塞,直到有描述符就绪,或超时,函数才返回。当函数返回以后,再遍历fdset来寻找就绪的描述符。select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。

有以下缺点:

  • 每次调用都需要把fd集合从用户态拷贝到内核态,这样会使得用户空间和内核空间在传递该结构时复制开销大;

  • 每次扫描时采取线性扫描内核中的所有fd,效率很低;

  • 单个线程处理的IO数量由fd限制,默认值为1024。

然而,它有良好的跨平台性,几乎所有平台都可以支持。

poll

poll和select的实现是类似的,区别在于它是基于链表存储的,没有最大连接的限制。

epoll

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

epoll的调用分为三个部分:调用epoll_create创建一个epoll对象、调用epoll_ctl向epoll对象中添加连接的套接字、调用epoll_wait收集发生了I/O事件的连接,epoll_wait 的效率会非常高,因为调用的时候只是传递了发生 IO 事件的连接。

当某一个进程调用 epoll_create 方法的时候,Linux 内核会创建一个 eventpoll 结构体,这个结构体中有两个重要的成员:

  • rb_root rbr:这是红黑树的根节点,存储着所有添加到 epoll 中的事件,也就是这个 epoll 监控的事件。
  • list_head rdllist: 这是一个双向链表,保存着将要通过 epoll_wait 返回给用户的、满足条件的事件。

每一个 epoll 对象都有一个独立的 eventpoll结构体,这个结构体会在内核空间中创造独立的内存,用于存储使用 epoll_ctl 方法向 epoll 对象中添加进来的事件。这些事件都会挂到 rbr 红黑树中,这样就能够高效的识别重复添加的节点。

所有添加到 epoll 中的事件都会与设备(如网卡等)驱动程序建立回调关系,也就是说,相应的事件发生时会调用这里的方法。这个回调方法在内核中叫做 ep_poll_callback,它把这样的事件放到 rdllist 双向链表中。在 epoll 中,对于每一个事件都会建立一个 epitem 结构体。

当调用 epoll_wait 检查是否有发生事件的连接时,只需要检查 eventpoll 对象中的 rdllist 双向链表中是否有 epitem元素,如果 rdllist 链表不为空,则把这里的事件复制到用户态内存中的同时,将事件数量返回给用户。通过这种方法,epoll_wait 的效率非常高。epoll-ctl 在向 epoll 对象中添加、修改、删除事件时,都是从 rbr这样的 红黑树中查找事件也非常快。这样,epoll 就能够轻易的处理百万级的并发连接。

针对于select的三个缺点,epoll有了更好的解决方法:

  1. 对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd直接拷贝进内核,这样避免了用户态和内核态之间的重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。

  2. 对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。

  3. 对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

如果没有大量的空闲连接或者“死”连接,epoll的效率并不会比select/poll高很多,但是当有大量空闲连接的时候,就会发现epoll的效率会大大提高。

它们的区别是什么呢?

三者的区别体现在三个方面:

类型 支持的最大连接数 FD剧增后带来的IO效率问题 消息传递方式
select 最大连接数由FD_SIZE决定,默认值为1024 每次调用都会对连接进行线性遍历,随着FD的增加使得遍历效率降低 通过拷贝的方式将内核的消息传递给用户空间
poll 基于链表存储,没有最大连接数的限制 同上 同上
epoll 最大连接数由内存决定,1G的内存可以打开约10万个连接 fd上是利用回调函数,只有活跃的socket才会主动调用callback,在活跃socket较少的情况下,不存在前者的线性下降的性能问题,当所有socket都很活跃的时候,可能会出现性能问题 通过共享内存的方式,实现内核态和用户态的通信

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容

  • IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步、异步、阻塞、非阻塞 同步(synchronous)...
    可笑的黑耀斑阅读 1,158评论 0 2
  • 一、什么是io多路复用在bio模型中,一个io请求对应一个线程,造成线程极大浪费,且没有数据发送的情况下,线程也一...
    刘建会阅读 1,727评论 1 1
  • 本文摘抄自linux基础编程 IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作。那么我们对与外部设...
    VD2012阅读 1,017评论 0 2
  • python之路——IO模型 IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步、异步、阻塞、非阻塞 ...
    go以恒阅读 542评论 0 2
  • I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O...
    飞扬code阅读 2,152评论 0 6