Linux IO 模型

更多 Java IO & NIO方面的文章,请参见文集《Java IO & NIO》


缓冲区

缓冲区的引入是为了减少频繁I/O操作而引起频繁的系统调用,当你操作一个流时,更多的是以缓冲区为单位进行操作,这是相对于用户空间而言。对于内核来说,也需要缓冲区。

几个基本概念

同步 IO VS 异步 IO 的区别:

  • 同步 IO:数据访问的时候进程会阻塞
  • 异步 IO:数据访问的时候进程不会阻塞

阻塞 IO 和非阻塞 IO 的区别:

  • 阻塞 IO:应用程序的调用不会立即返回
  • 非阻塞 IO:应用程序的调用会立即返回

同步 IO 包括:

  • 阻塞 IO Blocking IO
  • 非阻塞 IO NonBlocking IO
  • 多路复用 IO Multiplexing IO
  • 信号驱动 IO Signal Driven IO

阻塞 IO Blocking IO

Linux 下默认所有的 Socket 都是阻塞 IO

阻塞 IO 分为两个步骤:

  • 步骤 1. 等待数据准备,拷贝到 OS 内核缓存区 (该过程中应用程序进程都会被阻塞)
  • 步骤 2. 从 OS 内核缓存区拷贝到应用程序的地址空间 (该过程中应用程序进程都会被阻塞)

基本步骤如下,其中包括一次系统调用 recvfrom:

Blocking IO

非阻塞 IO NonBlocking IO

可以将 Socket 设置为非阻塞 IO,例如 Java NIO 中可以设置 SocketChannel:channel.configureBlocking(false);

在上述步骤 1 等待数据的过程中,应用程序进程不会被阻塞,而是不断询问 OS 内核数据有没有准备好:

  • 如果数据没有准备好,OS 内核返回一个 error,应用程序进程过一段时间再次询问(该过程中应用程序进程不会被阻塞)
  • 如果数据已经准备好,则进入上述步骤 2,将数据从 OS 内核缓存区拷贝到应用程序的地址空间(该过程中应用程序进程都会被阻塞)

基本步骤如下,其中包括多次系统调用 recvfrom:

NonBlocking IO

多路复用 IO Multiplexing IO

  • 单个进程可以同时处理多个网络连接的 IO,即监听多个端口的 IO
  • 适用于连接数很高的情况
  • 实现方式:select,poll,epoll 系统调用
    • 注册多个端口的监听 Socket,比如 8080,8081
    • 当用户进程调用 select 方法后,整个用户进程被阻塞,OS 内核会监听所有注册的 Socket
    • 当任何一个端口的 Socket 中的数据准备好了( 8080 或者 8081),select 方法就会返回
    • 随后用户进程再调用 read 操作,将数据从 OS 内核缓存区拷贝到应用程序的地址空间。
  • 多路复用 IO 类似于 多线程结合阻塞 IO
    • 要实现监听多个端口的 IO,还可以通过多线程的方式,每一个线程负责监听一个端口的 IO
    • 如果处理的连接数不是很高的话,使用 多路复用 IO 不一定比使用 多线程结合阻塞 IO 的服务器性能更好,可能延迟还更大
    • 多路复用 IO 的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接

多路复用 IO Multiplexing IO 的优点:

  • 对于耗时短的处理场景高效
  • OS 可以在多个事件源上等待,避免多线程结合阻塞 IO 方式带来的复杂度及性能开销
  • 事务分离,将与应用无关的多路分解和分配机制与应用相关的回调函数分离开

多路复用 IO Multiplexing IO 的缺点:

  • 处理耗时长的操作会造成事务分发的阻塞,影响后续事件的处理。

基本步骤如下,其中包括两次系统调用 select 和 recvfrom:

Multiplexing IO

具体的使用,可以参见 Java NIO Buffer, Channel 及 Selector 中所述的 Selector 选择器。

异步 IO Asynchronous IO

异步 IO 用的很少。

  • 用户进程发起异步 read 操作后,OS 内核立即返回,用户进程不会阻塞,而是去做其他事情。
  • OS 内核等待数据准备好,随后将数据从 OS 内核缓存区拷贝到应用程序的地址空间,随后给用户进程发送一个信号,用户进程接着处理数据。

select 及 epoll

阻塞I/O模式下,一个线程只能处理一个流的I/O事件。如果想要同时处理多个流,要么多进程(fork),要么多线程(pthread_create),很不幸这两种方法效率都不高。
非阻塞忙轮询的I/O方式,我们发现我们可以同时处理多个流了,我们只要不停的把所有流从头到尾问一遍,又从头开始。这样就可以处理多个流了,但这样的做法显然不好,因为如果所有的流都没有数据,那么只会白白浪费CPU。

为了避免CPU空转,可以引进了一个代理(一开始有一位叫做 select 的代理,后来又有一位叫做 poll 的代理,不过两者的本质是一样的)。这个代理比较厉害,可以同时观察许多流的I/O事件,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中醒来,于是我们的程序就会轮询一遍所有的流:

while true {
    select(streams[]) // 当前线程阻塞
    for i in streams[] { // 轮询一遍所有的流
        if i has data
            read until unavailable
    }
}

但是依然有个问题,我们从 select 那里仅仅知道了,有I/O事件发生了,但却并不知道是那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。

epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll之会把哪个流发生了怎样的I/O事件通知我们。
在讨论epoll的实现细节之前,先把epoll的相关操作列出:

  • epoll_create:创建一个epoll对象,一般 epollfd = epoll_create()
  • epoll_ctl :往epoll对象中增加/删除某一个流的某一个事件,比如:
    • epoll_ctl(epollfd, EPOLL_CTL_ADD, socket, EPOLLIN); 有缓冲区内有数据时epoll_wait返回
    • epoll_ctl(epollfd, EPOLL_CTL_DEL, socket, EPOLLOUT); 缓冲区可写入时 epoll_wait返回
  • epoll_wait(epollfd,...) :等待直到注册的事件发生

一个epoll模式的代码大概的样子是:

while true {
    active_stream[] = epoll_wait(epollfd) // 当前线程阻塞
    for i in active_stream[] { // epoll之会把哪个流发生了怎样的I/O事件通知我们
        read or write till unavailable
    }
}

引用:
我读过的最好的epoll讲解(转)

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

推荐阅读更多精彩内容

  • 上一篇《聊聊同步、异步、阻塞与非阻塞》[https://www.jianshu.com/p/aed6067eeac...
    七寸知架构阅读 139,609评论 58 444
  • 转自: http://www.jianshu.com/p/486b0965c296 http://www.jia...
    demop阅读 3,841评论 1 21
  • Linux下一切皆文件,连外部设备都当作文件;对文件的读写操作会调用内核系统命令,返回文件描述符,简称fd;而对s...
    coderlan阅读 294评论 2 7
  • 人生就是这样给不努力还死要面子的人当头一击。可能这对于别人没什么大不了,但这是我对于浑浑噩噩大学生活的一次警鸣!...
    乌云乌云阅读 261评论 0 0
  • 昨天和朋友交流,她问为什么好久没看你参加各种培训活动了呢? 我一愣,说:“现在管老又管小,还要忙工作...
    筱荀阅读 654评论 1 0