I/O 多路复用底层原理前篇 - 五种IO模型

点赞再看,养成习惯,公众号搜一搜【一角钱技术】关注更多原创技术文章。本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章。

前言

上篇讲 BIO、NIO、AIO 的基本概念以及一些常见问题,介绍了 NIO 是同步非阻塞 ,服务器实现模式为一个线程可以处理多个请求(连接),客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有IO请求就进行处理。那么I/O多路复用器到底是如何实现的?本篇我们来一探究竟。

为了加深对 I/O多路复用机制 的理解,以及了解到多路复用也有局限性,本着打破砂锅问到底的精神,在这里我们先回顾下 Unix网络编程中的五种IO模型。下篇再继续
IO多路复用进行深入的学习。

  • Blocking IO - 阻塞IO
  • NoneBlocking IO - 非阻塞IO
  • IO multiplexing - IO多路复用
  • signal driven IO - 信号驱动IO
  • asynchronous IO - 异步IO

Unix网络编程中的五种IO模型

阻塞IO - Blocking IO

最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。

当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。

也许有人会说,可以采用多线程+ 阻塞IO 来解决效率问题,但是由于在多线程 + 阻塞IO 中,每个socket对应一个线程,这样会造成很大的资源占用,并且尤其是对于长连接来说,线程的资源一直不会释放,如果后面陆续有很多连接的话,就会造成性能上的瓶颈。

非阻塞IO - NoneBlocking IO

当用户线程发起一个 IO 操作后,并不需要等待,而是马上就得到一个结果。如果结果是一个 error 时,它就知道数据还没有准备好,于是它可以再次发送 IO 操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。

在非阻塞IO 模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。

对于非阻塞IO就有一个非常严重的问题,在while循环中需要不断地去询问内核数据是否就绪,这样会导致CPU占用率非常高,因此一般情况下很少使用while循环这种方式来读取数据。

  • 非阻塞式主要体现在用户进程发起recvfrom系统调用的时候,这个时候系统内核还没有接收到数据报,直接返回错误给用户进程,告诉“当前还没有数据报可达,晚点再来”
  • 用户进程接收到信息,但是用户进程不知道什么时候数据报可达,于是就开始不断轮询(polling)向系统内核发起recvfrom的系统调用“询问数据来了没”,如果没有则继续返回错误
  • 用户进程轮询发起recvfrom系统调用直至数据报可达,这个时候需要等待系统内核复制数据报到用户进程的缓冲区,复制完成之后将返回成功提示

IO多路复用 - IO multiplexing

所谓 I/O 多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。这种机制的使用需要 selectpollepoll 来配合。

在多路复用IO模型中,会有一个内核线程不断地去轮询多个 socket 的状态,只有当真正读写事件发送时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有真正有读写事件进行时,才会使用IO资源,所以它大大减少来资源占用。

  • IO复用模式是使用select或者poll函数向系统内核发起调用,阻塞在这两个系统函数调用,而不是真正阻塞于实际的IO操作(recvfrom调用才是实际阻塞IO操作的系统调用)
  • 阻塞于select函数的调用,等待数据报套接字变为可读状态
  • 当select套接字返回可读状态的时候,就可以发起recvfrom调用把数据报复制到用户空间的缓冲区

信号驱动IO - signal driven IO

在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。这个一般用于UDP中,对TCP套接字几乎没用,原因是该信号产生得过于频繁,并且该信号的出现并没有告诉我们发生了什么请求。

用户进程可以使用信号方式,当系统内核描述符就绪时将会发送SIGNO给到用户空间,这个时候再发起recvfrom的系统调用等待返回成功提示,流程如下:

  • 先开启套接字的信号IO启动功能,并通过一个内置安装信号处理函数的signaction系统调用,当发起调用之后会直接返回;
  • 其次,等待内核从网络中接收数据报之后,向用户空间发送当前数据可达的信号给信号处理函数;
  • 信号处理函数接收到信息就发起recvfrom系统调用等待内核数据复制数据报到用户空间的缓冲区;
  • 接收到复制完成的返回成功提示之后,应用进程就可以开始从网络中读取数据。

异步IO - asynchronous IO

前面四种IO模型实际上都属于同步IO,只有最后一种是真正的异步IO,因为无论是多路复用IO还是信号驱动模型,IO操作的第2个阶段都会引起用户线程阻塞,也就是内核进行数据拷贝的过程都会让用户线程阻塞。

  • 由POSIX规范定义,告知系统内核启动某个操作,并让内核在整个操作包含数据等待以及数据复制过程的完成之后通知用户进程数据已经准备完成,可以进行读取数据;
  • 与上述的信号IO模型区分在于异步是通知我们何时IO操作完成,而信号IO是通知我们何时可以启动一个IO操作

总结

现代计算机服务器操作系统大部分都是基于linxu实现,为处理高并发而采取NIO的模型,对于支持异步IO模型的系统持有不确定因素。

详见 BIO 、NIO 、AIO 总结

同步与异步的定义

  • 同步:发起一个fn的调用,需要等待调用结果返回,该调用结果要么是期望的结果要么是异常抛出的结果,可以说是原子性操作(要么成功要么失败返回)
  • 异步: 发起一个fn调用,无需等待结果就直接返回,只有当被调用者执行处理程序之后通过“唤醒”手段通知调用方获取结果(唤醒的方式有回调,事件通知等)
  • 小结: 同步和异步关注的是程序之间的通信

阻塞与非阻塞的定义

  • 阻塞: 类比线程阻塞来说明,在并发多线程争抢资源的竞态条件下,如果有一个线程已持有锁,那么当前线程将无法获取锁而被挂起,处于等待状态
  • 非阻塞: 一旦线程释放锁,其他线程将会进入就绪状态,具备争抢锁的资格
  • 小结: 阻塞与非阻塞更关注是程序等待结果的状态
  • 由此可知,同步异步与阻塞非阻塞之间不存在关联,关注的目标是不一样的

同步IO与异步IO(基于POSIX规范)

  • 同步IO: 表示应用进程发起真实的IO操作请求(recvfrom)导致进程一直处于等待状态,这时候进程被阻塞,直到IO操作完成返回成功提示
  • 异步IO: 表示应用进程发起真实的IO操作请求(recvfrom)导致进程将直接返回一个错误信息,“相当于告诉进程还没有处理好,好了会通知你”
  • 阻塞IO: 主要是体现发起IO操作请求通知内核并且内核接收到信号之后如果让进程等待,那么就是阻塞
  • 非阻塞IO: 发起IO操作请求的时候不论结果直接告诉进程“不用等待,晚点再来”,那就是非阻塞

IO模型对比

  • 根据上述的同步与异步IO定义并结合上述的模型可知,只有异步IO模型符合POSIX规范的异步IO,其他IO模型都存在recvfrom系统调用被内核阻塞,属于同步IO操作

  • 由此可知,阻塞IO与非阻塞IO可总结如下:

  • 也就是说,要么称为同步与异步IO,要么称为上述5种模型的IO说法,注意上述的同步与异步的概念

  • 大部分操作系统都是基于同步IO的方式实现,对于支持异步IO模型的操作系统还不确定,在实际工作我们经常会说Blocking-IO(阻塞IO)和Non-Blocking-IO(非阻塞IO),极少称同步IO与异步IO

  • 小结: 同步与异步针对通信机制,阻塞与非阻塞针对程序调用等待结果的状态

一句话总结

  • 阻塞IO与非阻塞IO

这是最简单的模型,一般配合多线程来实现。

  • 多路复用(select/poll/epoll)

一个线程解决多连接的问题

  • 信号驱动IO模型

一种同步IO,更加灵活

  • 异步IO模型

高效主流的模型,效率很高。

文章持续更新,可以公众号搜一搜「 一角钱技术 」第一时间阅读,本文 GitHub org_hejianhui/JavaStudy 已经收录,欢迎 Star。

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