文章目录

  • 引入
    • 操作系统的内核态和用户态
    • 文件描述符fd
    • IO操作过程:
    • 阻塞和非阻塞
    • 同步和异步
      • 同步IO和异步IO
  • 五种IO模型
    • 1、(同步)阻塞IO模型
    • 2、(同步)非阻塞IO模型
    • 3、IO多路复用模型
    • 4.信号驱动IO模型
    • 5、异步IO模型

引入

在《Unix网络编程》一书中提到了五种IO模型,分别是:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO,其中前4种IO都属于同步IO。
这5种IO同时也被称为Linux的五种网络IO模型。由于服务器端一般都是使用的Linux操作系统,所以了解这几种io模型也是学习java网络io模型BIO,NIO,AIO的基础。

操作系统的内核态和用户态

在了解IO模型之前首先需要了解操作系统内核态和用户态的概念:用户态字面理解就是用户使用的空间,内核态则是操作系统使用的空间。在IO中(文件IO或者是网络IO),都是由用户去调用Read读取内核态中的数据,读取数据到用户态;而write则是将数据从用户态写到内核态中,由内核去写入文件或者是通过网络IO(网卡)发送数据。

文件描述符fd

内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。
实际上,文件描述符是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

IO操作过程:

对于一次IO操作(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:

  1. 第一阶段:等待数据准备(被拷贝到内核中)
  2. 第二阶段:将数据从内核拷贝到进程中

IO可分为文件IO和网络IO,对于网络IO来说,它的本质是socket的读取,socket在linux系统被抽象为流,IO可以理解为对流的操作。对于socket流而言:

  1. 第一步:通常涉及等待网络上的数据分组到达,然后被复制到内核的某个缓冲区。
  2. 第二步:把数据从内核缓冲区复制到应用进程缓冲区。

阻塞和非阻塞

接下来需要理解阻塞和非阻塞:
阻塞
阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。
非阻塞
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。当前线程可以去干别的事情。
阻塞IO和非阻塞IO的区别就在于: 应用程序的调用是否立即返回

同步和异步

同步
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回,它还会抢占cpu去执行其他逻辑,也会主动检测io是否准备好。
异步
异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

同步IO和异步IO

对于IO来说,同步IO和异步IO的区别就在于: IO操作的第二阶段,数据拷贝的时候进程是否阻塞。以read读操作为例:
同步IO:应用程序主动向内核查询是否有可用数据,如果有,当前进程自己负责把数据从内核copy到用户空间,拷贝的过程中进程阻塞。
异步IO:应用程序向内核发起读数据请求时需要:(1)告诉内核数据存放位置(2)注册回调函数,当内核完成数据copy后调用回调通知应用程序取数据。因为数据copy由内核完成的,所以拷贝的时候进程不阻塞。

换句话说,同步IO/异步IO最大区别:同步IO数据从内核空间到用户空间的copy动作是由应用程序自己完成。而异步IO则是注册回调函数并告知内核用户空间缓冲区存放地址,数据copy由内核完成。

参考:简述同步IO和异步IO的区别

五种IO模型

1、(同步)阻塞IO模型


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

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

2、(同步)非阻塞IO模型


当用户进程发起一个IO操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,进程在返回之后,可以干点别的事情,然后它可以再次发送IO操作。一旦内核中的数据准备好了,并且又再次收到了用户进程的请求,那么进程就会将内核中的数据拷贝到用户内存,然后返回。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态。

3、IO多路复用模型


所谓I/O多路复用机制,就是说通过一种机制,实现一个线程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。没有文件描述符就绪时会阻塞应用程序,交出cpu 。多路是指网络连接,复用指的是同一个线程。

这种机制的使用需要额外的功能来配合: select、poll、epoll。
select、poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的。

Linux 用 select/poll/epoll 函数实现 IO 复用模型,这些函数也会使进程阻塞,但是和阻塞IO所不同的是这些函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。直到有数据可读或可写时,才真正调用IO操作函数。

  • select时间复杂度O(n),它仅仅知道了,有I/O事件发生了,却并不知道是哪几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。
  • poll(翻译:轮询)时间复杂度O(n),poll本质上和select没有区别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是它没有最大连接数的限制,原因是它是基于链表来存储的.
  • epoll时间复杂度O(1),epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))。
    epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

select的三大缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历从用户态传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024

poll解决了第三个缺点,它没有最大连接数的限制,原因是它是基于链表来存储的,但它同样会有前两个缺点。

epoll的改进
epoll既然是对select和poll的改进,就应该能避免上述的三个缺点。那epoll都是怎么解决的呢?在此之前,我们先看一下epoll和select和poll的调用接口上的不同,select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。

对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd从用户态拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。在内核⾥使⽤「红⿊树」来关注进程所有待检测的 Socket,红⿊树是个⾼效的数据结构,增删查⼀般时间复杂度是 O(logn)

对于第二个缺点,epoll在epoll_ctl时为每个fd指定一个回调函数,当设备就绪,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表,,然后唤醒等待队列上的等待者。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd。

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

epoll的两种触发模式

  1. LT水平触发(默认): 只要监听的文件描述符fd中有数据,就会触发epoll_wait有返回值,不断的提醒用户程序去操作,直到内核缓冲区数据被 read函数读完才结束。这是默认的epoll_wait的方式;
  2. ET边缘触发:只有监听的文件描述符fd的读/写事件发生,缓冲区状态发生变化,才会触发epoll_wait有返回值,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读,所以在ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值,或者遇到EAGAIN错误。

epoll ⽀持边缘触发和水平触发的⽅式,而 select/poll 只⽀持⽔平触发,⼀般而言,边缘触发的方式会比水平触发的效率高。 因为边缘触发可以减少 epoll_wait 的系统调⽤次数,系统调⽤也是有⼀定的开销的的,毕竟也存在上下⽂的切换。

总结:
(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,而epoll只要一次拷贝。

参考:IO多路复用 && 五种IO模型
elect、poll、epoll优缺点
linux多路IO–epoll(一)–水平触发和边沿触发

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

4.信号驱动IO模型

Linux 用socket进行信号驱动 IO,用户线程发起一个IO请求操作会给对应的socket安装一个信号处理函数,进程继续运行并不阻塞,当IO事件就绪,进程收到SIGIO 信号,然后处理 IO 事件。

进程在第一个阶段是非阻塞,在第二个阶段是阻塞;和非阻塞IO有点相似,但它与非阻塞IO的区别在于它提供了消息通知机制,不需要用户进程不断的轮询检查,减少了系统API的调用次数,提高了效率。

这个一般用于UDP中,对TCP套接口几乎是没用的,原因是该信号产生得过于频繁,并且该信号的出现并没有告诉我们发生了什么事情。在UDP上,SIGIO信号会在下面两个事件的时候产生:
1 数据报到达套接字
2 套接字上发生错误
因此我们很容易判断SIGIO出现的时候,如果不是发生错误,那么就是有数据报到达了。
而在TCP上,由于TCP是双工的,它的信号产生过于频繁,并且信号的出现几乎没有告诉我们发生了什么事情。因此对于TCP套接字,SIGIO信号是没有什么使用的。

5、异步IO模型

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

Linux中,可以调用 aio_read 函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方
式,然后无论内核数据是否准备好,都会直接返回,用户态进程可以去做别的事情,当内核将数据拷贝到缓冲区之后,再通知应用程序。IO两个阶段,进程都是非阻塞的。

Linux提供了AIO库函数实现异步,但是用的很少。目前有很多开源的异步IO库,例如libevent、libev、libuv。

参考:聊聊Linux 五种IO模型
Linux的5种网络IO模型详解 这篇博客描述上有些问题

Linux/Unix五种IO模型相关推荐

  1. 一文弄懂Linux下五种IO模型

    Linux下主要的IO主要分为:阻塞IO(Blocking IO),非阻塞IO(Non-blocking IO),同步IO(Sync IO)和异步IO(Async IO). 同步:调用端会一直等待服务 ...

  2. 细节分析Linux中五种IO模型和三种实现方式

    I/O介绍 操作系统分为两种I/O 网络IO:本质是socket读取 磁盘IO:DMA操作读取 C/C++Linux服务器开发知识点 内容包括C/C++,Linux,Nginx,ZeroMQ,MySQ ...

  3. linux 五种IO模型 简介

    Linux下主要的IO主要分为:阻塞IO(Blocking IO),非阻塞IO(Non-blocking IO),同步IO(Sync IO)和异步IO(Async IO). 同步:调用端会一直等待服务 ...

  4. Linux 下的五种 IO 模型

    Linux 下的五种 IO 模型 来源:decaywood's Blog 概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2 ...

  5. 聊聊 Linux 中的五种 IO 模型

    聊聊 Linux 中的五种 IO 模型 2016/04/21 · IT技术 · 8 评论 · iO, 同步, 异步, 阻塞, 非阻塞 分享到:0 本文作者: 伯乐在线 - 陶邦仁 .未经作者许可,禁止 ...

  6. 详解Linux 五种IO模型

    原文:https://www.jianshu.com/p/486b0965c296 上一篇 同步.异步.阻塞.非阻塞 已经通俗的讲解了,要理解同步.异步.阻塞与非阻塞重要的两个概念点了,没有看过的,建 ...

  7. Linux 五种IO模型

    想快速了解,看文末总结. 1 概念说明# 在进行解释之前,首先要说明几个概念: 用户空间和内核空间 进程切换 进程的阻塞 文件描述符 缓存 IO 1.1 用户空间与内核空间## 现在操作系统都是采用虚 ...

  8. 深入聊聊Linux五种IO模型

    一.相关概念讲解 1.同步与异步 同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列.要么成功都成功,失败都失败,两个任务的状态可以 ...

  9. Linux五种IO模型性能分析

    转载:http://blog.csdn.net/jay900323/article/details/18141217     Linux五种IO模型性能分析 目录(?)[-] 概念理解 Linux下的 ...

最新文章

  1. 干货丨 从遗传算法到强化学习,一文介绍五大生物启发式学习算法
  2. 关于line-height的一些理解
  3. 6月17 表单验证
  4. 机器学习入门系列一(关键词:单变量线性回归,梯度下降法)
  5. [再寄小读者之数学篇](2014-06-22 求导数 [中国科学技术大学2014年高等数学B考研试题])...
  6. 阿里用户体验大师教你如何让产品更加触动人心
  7. 互联网寒冬!“996”为什么还没实行?我还等着早点下班呢!
  8. 转!!ftp的主动模式(port)与被动模式(PASV)
  9. js禁止鼠标滑轮_js实现鼠标滑动到某个div禁止滚动
  10. 【信仰充值中心】Firefox 97 后续更新一览
  11. 手机网络游戏系统架构图
  12. 【matlab】GPU 显卡版本与计算能力(compute capability)兼容性问题
  13. Confluence 6 访问日志脚本
  14. bat脚本:Java一键编译(Javac java)
  15. Java多线程:同步集合与同步锁
  16. server sql 去 反斜杠_mssql sqlserver 检索字段中是否包含反斜杠的方法
  17. 抱歉,我又可以了。。。
  18. 了解CSS的float高度坍塌的原理,并懂得怎么解决高度坍塌!
  19. 【百度seo建议】网站快速被蜘蛛抓取方法
  20. #L190616楼市穿越与未来锚点

热门文章

  1. 物体运动-jQuery-图标向上运动再从下方出现运动到原位置
  2. NLP经典论文:ELMo 笔记
  3. shiro mgt包下的AbstractRememberMeManager类
  4. 小散量化炒股记|如何用MySQL搭建本地股票量化数据库
  5. 移动端触发touchend后阻止click事件
  6. 化妆品行业的数字进化论:S2B2B电商网站如何助力化妆品企业打造增长新动能
  7. Win11新电脑如何进行磁盘分区?
  8. 一只羊的奥秘,您可知羊是有四个胃的噢
  9. 华为7c手机怎么恢复出厂设置_华为交换机怎样恢复出厂设置.doc
  10. 理解HTTP缓存和304状态码