I/O分类
I/O分为内存I/O,网络I/O和磁盘I/O三种,通常我们说的I/O是后两者。
I/O简介
一个输入操作分为两个阶段:
等待数据就绪
数据从内核空间复制到用户空间
I/O模型阻塞还是非阻塞,同步还是异步,将会围绕上面两个阶段来进行讨论。
I/O模型分为五种IO模型,分别为阻塞I/O模型、非阻塞I/O模型、多路复用I/O模型、信号驱动I/O模型和异步I/O模型。前四种被称为同步I/O,下面对每种I/O模型进行详细分析。
阻塞I/O模型
阻塞I/O模型的通信过程如下图所示:
阻塞I/O模型的通信过程图
当应用程序调用了recvfrom这个系统调用,对于网络I/O来说,很多时候数据在一开始还没有到来,这时内核进入了第一阶段:等待数据就绪,应用程序就一直被阻塞。直到数据到来,并且数据从内核拷贝到用户空间,应用程序才被解除阻塞状态。
Java中accept和read方法分别对应着accept和recvfrom系统调用,这两个方法都是阻塞的,此次出现了三个版本的演进:单线程版-》多线程版-》线程池版
对比 | 概念 | 优点 | 痛点 |
单线程版 | 主线程同时处理接收socket连接和数据读取操作 | 单个线程,开发难度低 | 数据未到达时,read方法一直阻塞主线程,影响新socket连接进入 |
多线程版 | 主线程只处理接收socket连接,一直创建新线程处理数据读取操作 | 多线程,新socket连接可以进入 | 线程数过多,非常消耗内存;频繁上下文切换,浪费cpu资源 |
线程池版 | 主线程只处理接收socket连接,避免过多线程,将由线程池来处理数据读取操作 | 有限线程数 | 由于阻塞原因,在高并发环境,同样还是会影响新socket连接进入 |
阻塞I/O模型总结:
特点 | I/O两阶段都是阻塞的(顺序模式) |
优点 | 进程阻塞过程中,不消耗cpu;开发难度低;适合并发小的应用 |
痛点 | 不适合高并发场景;由于阻塞,需要为每个请求分配一个线程来处理 |
非阻塞I/O模型
非阻塞I/O模型的通信过程如下图所示:
非阻塞I/O模型的通信图
应用程序一直轮询调用recvfrom系统调用问内核有没有数据,没有数据返回error,不再阻塞。反之,应用程序也需要调用recvfrom,才能把数据从内核拷贝到用户空间。
非阻塞I/O模型总结:
特点 | 应用程序需要不断地主动询问内核数据是否就绪(轮询模式) |
优点 | 不再阻塞,单个或有限个线程可以进行新连接接收和数据读取操作 |
痛点 | 一直轮询重复调用,浪费cpu资源。比如:遍历1W个连接只有1个连接有数据待处理,其余的9999调用就是无效调用 |
多路复用I/O模型
多路复用I/O模型的通信过程如下图所示:
多路复用I/O模型的通信图
多个连接I/O注册到一个复用器(Selector)上提交给内核,内核帮忙监听I/O是否有数据到达,此时应用程序一直阻塞等待。当数据到达后,应用程序调用recvfrom将内核数据拷贝到用户空间。
I/O多路复用分为select,poll和epoll三种机制。
select和poll差不多,每次都将全量I/O注册到内核进行监听,当任意一个IO数据就绪时立刻返回,应用程序需遍历全量IO才能知道哪个IO就绪。参考:
https://man7.org/linux/man-pages/man2/poll.2.html#EXAMPLES
epoll是每次增量IO注册到内核,数据就绪时,只返回就绪IO。
三种机制对比:
对比项 | select | poll | epoll |
kernel版本 | version<2.4 | 2.4<=version<2.6 | version>=2.6 |
数据结构 | bitmap | 数组 | 红黑树 |
最大连接数 | 1024 | 无上限 | 无上限 |
fd拷贝 | 每次调用select拷贝 | 每次调用poll拷贝 | fd首次调用epoll_ctl拷贝,每次调用epoll_wait不拷贝 |
工作效率 | 轮询:O(n) | 轮询:O(n) | 回调:O(1) |
多路复用I/O模型总结:
特点 | 应用程序全量/增量将IO注册到内核,内核监听数据是否就绪(长连接模式) |
优点 | 相对非阻塞I/O模型,释放了CPU。 适合高并发的场景 |
痛点 | 需要一个线程阻塞等待数据就绪 |
信号驱动I/O模型
信号驱动I/O模型的通信过程如下图所示:
信号驱动I/O模型的通信图
应用程序向内核注册个信号处理函数,不用在阻塞等待,可以干别的事情了,当数据就绪时,内核发送信号到信号处理函数。应用程序可以在信号处理函数中调用recvfrom将拷贝到用户空间。
信号驱动I/O模型总结:
特点 | 应用程序不在阻塞,内核回调注册信号处理函数(函数回调模式) |
优点 | 数据就绪阶段不用阻塞等待 |
痛点 | 未能异步IO |
异步I/O模型
异步I/O模型的通信过程如下图所示:
异步I/O模型的通信图
应用程序发起aio_read操作后,给内核传递与read相同的描述符、缓冲区指针、缓冲区大小三个参数及文件偏移,当内核操作完成后,数据已经拷贝到用户空间了,应用程序拿到数据可直接处理了。linux系统AIO底层实现仍是用epoll,没有很好地实现AIO,在性能上没有明显优势,这也是Netty使用NIO而不是AIO的原因之一。
总结:
同步I/O:阻塞I/O模型、非阻塞I/O模型、多路复用I/O模型、信号驱动IO模型
异步I/O:异步IO模型
个人见解:
阻塞I/O模型--》顺序模式
非阻塞I/O模型--》轮询模式
多路复用I/O模型--》长连接模式
信号驱动IO模型--》函数回调模式
共同学习,写下你的评论
评论加载中...
作者其他优质文章