从IO模型到epoll

引子

上一篇《从网卡到TCP/IP协议栈的数据流转》,描述了数据从网卡到应用程序的流转过程

应用程序并不直接同内核交互来传递数据,而是通过缓冲区

如果是网络数据缓冲区,那么使用socket。如果是文件缓冲区,那么使用句柄

当网卡接收到数据之后,内核会将数据拷贝到与四元组连接对应的socket缓冲区

那么应用程序如何感知到数据到达呢?

能不能直接读取socket缓冲区中的数据呢?

横梗在应用程序空间与内核空间之间的屏障

linux使用虚拟内存机制,应用进程维护了自己的虚拟内存地址空间,内核也维护了自己的内存地址空间,

应用程序不能直接通过指针来访问到内核地址空间之中的数据,数据的交互需要内核的参与

注:内核可以使用CPU,也可使用DMA。NIO中的零拷贝,就是不通过CPU而是通过DMA拷贝数据。关于零拷贝,后面再讲

应用程序与内核的IO交互

回顾socket监听代码

ServerSocket monitorSocket =new ServerSocket();
//绑定端口,指定需要监听的端口
monitorSocket.bind(8080);
//在此端口上开启监听
//通知内核,此socket需要监听8080端口,实际上是将监听socket信息注册到了内核维护的一个监听列表
//当有客户端与服务端握手时,服务端会先收到syn报文,然后去检查对用的端口上有没有应用程序在监听
//如果用应用程序在监听,才可能(还需要做其它检查)返回ack报文,否则返回rst报文,拒绝连接
monitorSocket.listen();
//通过监听socket调用accept函数,返回一个已经创建好连接的socket
//对应三次握手中的第三次握手,客户端返回ack之后,服务端初始化socket,并创建缓冲区
//内核处理tcp连接时,维护了两个队列,一个是正在握手的syn队列,一个是已经握手完成的accept队列
//accept函数就是从accept队列中获取一个已握手完成的tcp连接所对应的socket
Socket channelSocket = socket.accept();

//读取连接socket上的数据
channelSocket.read();

当应用程序获取到一个channelSocket后,需要不断地去读取socket上的数据

read()函数就会触发向内核询问的操作,因为应用程序不能直接判断socket上是否有数据到达,需要通过内核

内核空间中维护了一个数据结构(select,poll,epoll使用不同的数据结构,后面再讲)

也就是说,应用程序读取数据的过程如下

  1. 向内核发起读取请求

  2. 内核将数据返回

讲到这里,IO模型就呼之欲出了

IO模型

同步阻塞式IO(blocking IO)

阻塞式IO模型.png

应用程序通过channelSocket向内核请求读取数据,内核发现此channelSocket没有新的数据,那么线程会阻塞,直到有数据返回

同步非阻塞式IO(noblocking IO)

非阻塞式IO模型.png

应用程序通过channelSocket向内核请求读取数据,内核检查如果没有数据,则直接返回,告诉应用程序“没有”

多路复用IO(multiplexing IO)

IO多路复用模型.png

应用程序有多个chanelSocket,把所有的chanelSocket的读取操作交给一个线程去完成

也就是把读取操作分为了两步,向内核查询可读取的socket,向内核执行读取

  1. 应用程序使用一个线程通过chanelSocket列表向内核发起读取操作,线程需要等待内核的响应,然后返回可读条件

  2. 应用程序根据可读条件向内核发起数据读取请求,此时内核再通过CPU或者DMA将数据拷贝到用户空间

可以看到,如果要支持多路复用,需要内核一次遍历多个ChanelSocket,并返回可读条件,这需要内核的支持

现代内核支持的select,poll,epoll模型都是基于多路复用的

异步IO (asynchronous IO)

异步IO模型.png
  1. 应用程序首先给内核发送一个读取信号,内核马上返回,表示收到

  2. 当有channelSocket的数据就绪时,内核根据注册的aio信号,直接把数据拷贝到用户空间,并返回aio注册时的标识

这种IO方式时完全异步,非阻塞的,但是需要内核更进一步的支持。内核收到aio信号后,需要监听对应的sockt.

也就是把数据就绪检查的工作交给了内核去实现

现在大多数linux内核都不支持AIO,不过Windows下的IOCP已支持AIO模型

主流IO模型

理论上最快的AIO并不被大多数linux内核所支持,因此使用最多的是 多路复用的IO模型

多路复用IO实际是

  1. 同步:调用内核需要的函数需要等待内核的响应

  2. 非阻塞的:第一次调用只查询就绪列表,而不用等待数据读取

所以要全面描述的话,叫做同步非阻塞多路复用IO

select &poll &epoll

之前有讲到,select,poll,epoll模型都是多路复用的IO模型,那么它们之间的区别在哪里呢?

多路复用实现的方式不同,具体的说,查询channelSocket就绪列表的实现方式不同

select /poll

  1. 应用程序首先调用内核接口,传入一个需要查询的chanelSocket列表

  2. 内核收到后调用请求,根据channelSocket列表去遍历内核中的socketChannel列表,逐个检查channelSocket是否可读。然后返回一个可读的chanelScoket列表给到应用程序

select 和poll的区别仅仅在于 :

  • select使用固定长度bitMap来保存socketChannel列表,固定BiteMap,能维护的sockChannel受限

  • poll使用动态数据来保存socketChannel列表

总的来说,有两点缺点

  1. chanelSocket列表需要做两次拷贝,第一次从用户态拷贝到内核态,第二此从内核态拷贝到用户态

  2. 每一次查询都需要遍历所有的channelSocket

epoll

针对select 和poll的问题,epoll做了以下两点改进

  1. 在内核中使用红黑树来维护channelSockt列表,增删改查的时间复杂度都为O(logn)

  2. epoll使用事件驱动的机制,当有channelSocket就绪时,通过回调函数将channelSocket加入到就绪列表中,而不需要遍历

epoll的事件驱动机制,不会随着监听的channeSockt增多而提高延时

什么是零拷贝

定义

这个词在现在大多数高性能架构中都能听到,而且很容易误解

先明确定义:

不需要 CPU 参与的数据拷贝才叫做零拷贝

CPU参与的拷贝

举例

  1. 当数据需要从内核态拷贝到用户态时,内核发起CPUI中断,cpu放下手中的事情响应中断请求,完成拷贝的操作

  2. 当数据从通过网卡检验,网卡缓冲,需要进入socket缓冲区时,网卡驱动发起中断,CPU响应中断,完成数据拷贝操作

  3. ....

可以看到,数据的转移需要拷贝,而拷贝需要CPU响应大量的中断来参与,有如下缺点

  1. CPU 响应中断,有大量的线程切换开销

  2. CPU参与数据拷贝,污染CPU高速缓冲,影响CPU的执行

  3. 数据拷贝非常简单,CPU的参与大材小用,无法发挥CPU高速计算的优势

那有没有什么东西可以分担CPU这项数据拷贝操作呢?

DMA参与的拷贝

DMA的全程叫做 direct memory access 直接存储器访问

可以将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器与存储器之间的高速数据传输

不需要CPU的参与,即可完成数据的拷贝操作

DMA在现代计算机中通常作为一个组件集成在CPU上

零拷贝

DMA不仅可以替代CPU的数据拷贝工作,因为可以直接在存储器之间高速数据传输,甚至还可以节省数据拷贝路径

如:一个请求静态资源的过程

DMA参与之前:

磁盘文件->页缓存->mmap到用户空间->socket缓冲区->网卡缓冲区

DMA参与之后: 磁盘文件->页缓存->mmap到用户空间->网卡缓冲区

总结

介绍了很多种IO模型,主流的在使用的时多路复用

介绍了很多种多路复用的实现方式,主流的是epoll

那介绍这么多的必要性有吗?

有,知其然,知其所以然

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

推荐阅读更多精彩内容