[TOC]
一、IO流程
就是对于 Linux 系统, I/O 操作不是一步完成的。此处的 I/O 操作是一个通用型的概念,对于 socket 通信,也可以看作一个 I/O 操作过程,只不过操作的是网络对象。
I/O 操作一般分为两个部分:
- 应用程序发起 I/O 操作请求,等待数据,或者将要操作的数据拷贝到系统内核中(比如 socket)。
- 系统内核进行 I/O 操作(一般是内核将数据拷贝到用户进程中)。
阻塞和非阻塞
首先明确一点: 阻塞和非阻塞发生在请求处,关注的是程序在等待调用结果时的状态 。
通过上面的概念可以很容易的理解以下结论:
- 阻塞调用是指 调用结果返回之前,当前进程(线程)会被挂起 。调用进程(线程)阻塞在 I/O 操作请求处,直到 I/O 操作请求完成,数据到来,最重要的是用户进程的函数在请求的过程中不会返回。
- 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前进程(线程),**进程(线程)**可以去干别的事情。一般使用轮询的方式来查询 I/O 操作数据是否准确好了。
理解上面概念的一个要点是 请求的结果是否立即返回 ,同时需要注意的是,结果立即返回,不代表 I/O 操作完成,阻塞和非阻塞只关注请求是否立即获得结果。
同步和异步
同样需要明确一点: 同步和异步关注的是消息通信机制,具体来说就是调用者是否等待调用结果的返回,对于 I/O 操作而言,就是应用程序是否等待 I/O 操作完成 。
注意:此处的 I/O 操作一般是指上文中 I/O 操作中的两部分的第二部分。
同步和异步其实就是指 I/O 操作的第二部分,也就是进行具体 I/O 操作过程中,用户进程是否等待 I/O 操作结果返回。
- 阻塞和非阻塞是指进程访问的数据如果尚未就绪,进程是否需要等待,简单说这相当于函数内部的实现区别,也就是未就绪时是直接返回还是等待就绪。
- 同步和异步是指访问数据的机制,同步一般指主动请求并等待 I/O 操作完毕的方式,当数据就绪后在读写的时候必须等待,异步则指主动请求数据后便可以继续处理其它任务,随后等待 I/O,操作完毕的通知,这可以使进程在数据读写时也不阻塞。
二、IO模型
2.1 阻塞IO
用户线程发起一次read,由用户空间切换到内核空间,但是可能数据还没发送过来,该read方法就会阻塞住,等到复制(网卡->内存)完数据后,再从内核切换到用户线程。这里的阻塞即用户线程被阻塞。
从上面可以看到线程在两个阶段发生了阻塞:
- CPU把数据从磁盘读到内核缓冲区。
- CPU把数据从内核缓冲区拷贝到用户缓冲区。
2.2 非阻塞IO
非阻塞IO发出read请求后发现数据没准备好,会继续往下执行,此时应用程序会不断轮询polling内核询问数据是否准备好,当数据没有准备好时,内核立即返回EWOULDBLOCK错误。直到数据被拷贝到应用程序缓冲区,read请求才获取到结果。
需要注意的是最后一次 read 调用获取数据的过程,是一个同步的过程,是需要等待的过程。
这里的同步指的是 内核态的数据拷贝到用户程序的缓存区 这个过程。
这种方法并没有特别好的地方,它会牵扯到多次用户空间到内核空间的转换,切换太频繁会影响性能。
2.3 IO多路复用
IO多路复用的原文叫 I/O multiplexing
,这里的 multiplexing
指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流。
目的是尽量多的提高服务器的吞吐能力。实现一个线程监控多个IO请求,哪个IO有请求就把数据从内核拷贝到进程缓冲区,拷贝期间是阻塞的。
多路复用与阻塞IO的区别
- 阻塞IO模式下, 若线程因accept事件被阻塞,发生read事件后,仍需等待accept事件执行完成后 ,才能去处理read事件;
- 多路复用模式下,一个事件发生后,若另一个事件处于阻塞状态,不会影响该事件的执行。
2.4 异步IO
上面的提到过的操作都不是真正的异步,因为两个阶段总要等待会儿,而真正的异步 I/O 是内核数据准备好和数据从内核态拷贝到用户态这两个过程都不用等待。
很庆幸,Linux给我们准备了aio_read跟aio_write函数实现真实的异步,当用户发起aio_read请求后就会自动返回。内核会自动将数据从内核缓冲区拷贝到用户进程空间,应用进程啥都不用管。
三、同步和异步详解
3.1 同步
同步跟异步的区别在于数据从内核空间拷贝到用户空间是否由用户线程完成,这里又分为同步阻塞跟同步非阻塞两种。
- 同步阻塞 :此时一个线程维护一个连接,该线程完成数据从读写到处理全部过程,数据读写时线程是被阻塞的。
- 同步非阻塞 :非阻塞的意思是用户线程发出读请求后,读请求不会阻塞当前用户线程,不过用户线程还是要不断的去主动判断数据是否准备OK了。此时还是会阻塞等待内核复制数据到用户进程。他与同步BIO区别是使用一个连接全程等待
以 同步非阻塞 为例,如下可看到,在将数据从内核拷贝到用户空间这一过程,是由用户线程阻塞完成的。
3.2 异步
对于异步来说,用户进行读或者写后,将立刻返回,由内核去完成数据读取以及拷贝工作,完成后通知用户,并执行回调函数(用户提供的callback),此时数据已从内核拷贝到用户空间,用户线程只需要对数据进行处理即可,不需要关注读写,用户不需要等待内核对数据的复制操作,用户在得到通知时数据已经被复制到用户空间。
以如下的真实异步非阻塞为例:
可发现,用户在调用之后会立即返回,由内核完成数据的拷贝工作,并通知用户线程,进行回调。
3.3 同步跟异步对比
同步关注的消息通信机制synchronous communication
,在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。
异步关注消息通信机制asynchronous communication
,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。
附参考文章链接:
https://murphypei.github.io/blog/2018/05/io-block-sync
https://os.51cto.com/article/648226.html
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。