继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Netty源码-一分钟掌握I/O模型

HQGDD
关注TA
已关注
手记 3
粉丝 1
获赞 0

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模型的通信过程如下图所示:

http://img.mukewang.com/60d1f50d00014c8a12530603.jpg

阻塞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模型的通信过程如下图所示:

http://img2.mukewang.com/60d1f50d0001efee12800594.jpg

非阻塞I/O模型的通信图

应用程序一直轮询调用recvfrom系统调用问内核有没有数据,没有数据返回error,不再阻塞。反之,应用程序也需要调用recvfrom,才能把数据从内核拷贝到用户空间。

非阻塞I/O模型总结:

特点

应用程序需要不断地主动询问内核数据是否就绪(轮询模式)

优点

不再阻塞,单个或有限个线程可以进行新连接接收和数据读取操作

痛点

一直轮询重复调用,浪费cpu资源。比如:遍历1W个连接只有1个连接有数据待处理,其余的9999调用就是无效调用

多路复用I/O模型

多路复用I/O模型的通信过程如下图所示:

http://img4.mukewang.com/60d1f50e0001d04e12800584.jpg

多路复用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模型的通信过程如下图所示:

http://img3.mukewang.com/60d1f50e0001b12612800636.jpg

信号驱动I/O模型的通信图

应用程序向内核注册个信号处理函数,不用在阻塞等待,可以干别的事情了,当数据就绪时,内核发送信号到信号处理函数。应用程序可以在信号处理函数中调用recvfrom将拷贝到用户空间。

信号驱动I/O模型总结:

特点

应用程序不在阻塞,内核回调注册信号处理函数(函数回调模式)

优点

数据就绪阶段不用阻塞等待

痛点

未能异步IO

异步I/O模型

异步I/O模型的通信过程如下图所示:

http://img1.mukewang.com/60d1f50e0001f96512800618.jpg

异步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模型--》函数回调模式


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP