五分钟掌握同步异步I/O同步阻塞同步非阻塞异步阻塞异步非阻塞
网络通讯原理
在讲I\O的时候,先简单讲下网络通讯原理。
网络通讯四要素:本机的IP地址、子网掩码、网关的IP地址和DNS的IP地址。
四要素有两种方式:一是静态获取,即手动配置;二是动态获取,即通过DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)获取
网络通讯的过程
- 浏览器解析网址(IP地址+协议+端口号),通过访问DNS域名系统服务器(基于UDP)获得IP地址
- 生成HTTP消息并转交给Socket抽象层
- 再经过传输层,网络层,链路层等。细节先省略。可以参照:网络通讯的的整体流程
Socket起源于UNIX,在UNIX“一切皆文件”的哲学思想下,Socket是一种从打开,到完成读、写操作,最后关闭的模式,服务器和客户端各自维护一个“文件”,在建立连接打开文件后,可以向自己的文件写入内容供对方读取或者读取对方的内容,通信结束时关闭文件。
I/O
用户进程中的一次完整I/O交互流程分为两阶段,首先是经过内核空间,也就是由操作系统处理;紧接着就是到用户空间,也就是交由应用程序。
通俗地将,可以将I/O分为两步:第一步是等待;第二步是数据搬迁。
阻塞I/O模型
阻塞I/O模型特点:在I\O执行的两个阶段(等待数据和拷贝数据)都被阻塞。
下图中红色部分都是阻塞。
典型应用:阻塞Socket、Java BIO
优点:阻塞挂起不消耗CPU资源
非阻塞I/O模型
相比较阻塞I/O,多了一个轮询访问内核是否存在。
典型应用:Socket设置NON_BLOCK
缺点:轮询调用,消耗CPU资源
多路复用I/O模型
多路复用I/O模型和阻塞I/O模型并没有太大的不同,事实上,还更差一些,因为这里需要使用两次系统调用(select和recvfrom),而阻塞I/O模型只有一次系统调用(recvfrom)。但是,用Selector的优势在于它可以同时处理多个连接,所以如果处理的连接数不是很多,多路复用I\O模型并不一定比使用多线程加阻塞I\O性能要好,可能延时更大。优势在于能处于更多的连接。
典型应用:Java NIO,Nginx(epoll,poll,select)
信号驱动I\O模型
信号驱动I/O是指进程预先告知内核,向内核注册一个信号处理函数,然后用户进程返回不阻塞,当内核数据就绪时会发送一个信号给进程,用户进程便在信号处理函数中调用I/O读取数据。
应用场景比较少。
异步I/O模型
工作机制:告知内核启动某个操作,并让内核在整个操作完成后通知我们。
与信号驱动I/O模型的区别:信号驱动I/O模型是由内核通知我们何时可以启动一个I/O操作,这个I/O操作由用户自定义的信号函数来实现,而异步I/O模型由内核告知我们I/O操作何时完成
典型应用:Java 7 AIO,高性能服务器应用
不阻塞,数据一步到位,采用Proactor模式
缺点:需要操作系统底层支持,Linux2.5内核首现 2.6产的内部标准特性
易于混淆的概念
- 同步阻塞
- 同步非阻塞
- 异步阻塞
- 异步非阻塞
同步和异步的区别:
同步:应用程序主动发起请求询问状态(轮询)
异步:服务方会主动通知请求方
阻塞和非阻塞的区别:
阻塞:等待返回数据之前,当前的线程是挂起状态
非阻塞:等待返回数据之前,当前的线程是运行状态,可以继续处理其他任务
列子:
(1)这个时候,如果我们一直在店里面什么都不干,一直等待直到洗完照片,这个过程就叫同步阻塞。
(2)当然,大部分人很少这么干,更多的是大家拿起手机开始看电视,看一会儿就会问老板洗完没,老板说没洗完,然后接着看,再过一会儿接着问,直到照片洗完,这个过程就叫同步非阻塞。
(3)由于店里生意太好了,越来越多的人过来拍,店里面快没地方坐了,老板说你把手机号留下,我一会儿洗好了就打电话告诉你过来取,然后你去外面找了一个长凳开始躺着睡觉等待老板打电话,什么都不干,这个过程就叫异步阻塞(实际不应用)。
(4)当然实际情况是,大家可能会先去逛街或者吃饭,或者做其他活动,这样一来,两不耽误,这个过程就叫异步非阻塞(效率最高)。
各I/O模型的对比与总结
阻塞程度:阻塞I/O>非阻塞I/O>多路复用I/O>信号驱动I/O>异步I/O,效率是由低到高的。
BIO、NIO、AIO
BIO:一个连接只用一个线程来处理。
NIO:单线程管理多个连接。缓冲区(Buffer)、选择器(Selector)和通道(Channel)
AIO:异步I/O,把I/O读写操作完全交给操作系统。操作系统主动通知实现异步。
BIO | NIO | AIO |
---|---|---|
面向流 | 面向缓冲区 | 操作系统主动通知用户线程 |
阻塞I\O | 非阻塞I\O | Proactor设计模式 |
参考资料
[1]: Netty 4核心原理与手写RPC框架实战