Skip to content

Commit cd76df1

Browse files
author
leelovejava
committed
第三季 IO操作执行流程
1 parent ef6bb64 commit cd76df1

8 files changed

Lines changed: 108 additions & 0 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,18 @@
466466

467467
102、一个对技术有追求的面试官,是怎么深挖网络与IO的面试连环炮的?
468468

469+
103、Netty的架构原理图能画一下吗,他是如何体现Reactor架构思想的?
470+
471+
104、能说说你对堆外内存的理解吗?堆外内存的优势在哪里?
472+
473+
105、JDK是如何对堆外内存进行分配和回收的?会发生堆外内存溢出吗?
474+
475+
[106、如果不使用零拷贝技术,普通的IO操作在OS层面是如何执行的?](/docs/03/106.md)
476+
477+
[107、听说过mmap吗?内存映射技术为什么可以提升IO性能?](/docs/03/107.md)
478+
479+
[108、零拷贝技术到底是什么,他是如何提升IO性能的?](/docs/03/108.md)
480+
469481
### 分布式架构
470482

471483
### 中间件系统

docs/03/106.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# 106、如果不使用零拷贝技术,普通的IO操作在OS层面是如何执行的?
2+
3+
![一个普通IO操作的底层原理](images/106/01.png)
4+
5+
![IO操作执行流程](images/106/02.png)
6+
7+
```text
8+
File file = new File("xxx.txt");
9+
RandomAccessFile raf = new RandomAccessFile(file, "rw");
10+
11+
byte[] arr = new byte[(int) file.length()];
12+
# read读取数据,用户态切换内核态
13+
raf.read(arr);
14+
15+
16+
Socket socket = new ServerSocket(8080).accept();
17+
#
18+
socket.getOutputStream().write(arr);
19+
```
20+
21+
1) 内核态切换到用户态
22+
使用read读取数据的时候,会有一次用户态到内核态的切换,也就是说从用户角度切换到了内核角度去执行,这个时候基于DMA引擎把磁盘上的数据拷贝到内核缓冲里去;
23+
24+
2) 内核态切换到用户态
25+
接着会从内核态切换到用户态,基于CPU把内核缓冲里的数据拷贝到用户缓冲区里去
26+
27+
3) 用户态切换到内核态
28+
接着我们调用了Socket的输出流的write方法,此时会从用户态切换到内核态,同时基于CPU把用户缓冲区里的数据拷贝到Socket缓冲区里去,接着会有一个异步化的过程,
29+
30+
基于DMA引擎从Socket缓冲区里把数据拷贝到网络协议引擎里发送出去
31+
32+
4) 内核态切换回用户态
33+
都完成之后,从内核态切换回用户态
34+
35+
5) 总结
36+
37+
所以说,从本地磁盘读取数据,到通过网络发送出去,用户态和内核态之间,要发生4次切换,这是其一;
38+
39+
其二,数据从磁盘拿出来过后,一共要经过4次拷贝;
40+
41+
所以说,这4次切换和4次拷贝,让普通的IO操作都性能较低

docs/03/107.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# 107、听说过mmap吗?内存映射技术为什么可以提升IO性能?
2+
3+
![内存映射](images/107/01.png)
4+
5+
**一个磁盘文件映射到内存里来**,然后把映射到内存里来的数据通过socket发送出去
6+
7+
8+
有一种mmap技术,也就是内存映射,直接将磁盘文件数据映射到内核缓冲区,这个映射的过程是基于DMA引擎拷贝的,
9+
10+
同时用户缓冲区是跟内核缓冲区共享一块映射数据的,建立共享映射之后,就不需要从内核缓冲区拷贝到用户缓冲区了
11+
12+
光是这一点,就可以避免一次拷贝了,但是这个过程中还是会用户态切换到内核态去进行映射拷贝,接着再次从内核态切换到用户态,建立用户缓冲区和内核缓冲区的映射
13+
14+
接着把数据通过Socket发送出去,还是要再次切换到内核态
15+
16+
17+
18+
接着直接把内核缓冲区里的数据拷贝到Socket缓冲区里去,然后再拷贝到网络协议引擎里,发送出去就可以了,最后切换回用户态
19+
20+
21+
22+
**减少一次拷贝**,但是并不减少切换次数,一共是4次切换,3次拷贝
23+
24+
25+
26+
mmap技术是主要在RocketMQ里来使用的,公众号:狸猫技术窝,《从0开始带你成为消息中间件高手》的专栏,RocketMQ,里面剖析了一下,RocketMQ底层主要就是基于mmap技术来提升了磁盘文件的读写,性能

docs/03/108.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# 108、零拷贝技术到底是什么,他是如何提升IO性能的?
2+
3+
![零拷贝原理](images/108/01.png)
4+
5+
linux提供了sendfile,也就是零拷贝技术
6+
7+
8+
9+
在你的代码里面,如果说你基于零拷贝技术来读取磁盘文件,同时把读取到的数据通过Socket发送出去的话,流程如下,Kafka源码,transferFrom和transferTo两个方法,从磁盘上读取文件,把数据通过网络发送出去
10+
11+
12+
13+
这个零拷贝技术,就是先从用户态切换到内核态,在内核态的状态下,把磁盘上的数据拷贝到内核缓冲区,同时从内核缓冲区拷贝一些offset和length到Socket缓冲区;接着从内核态切换到用户态,从内核缓冲区直接把数据拷贝到网络协议引擎里去
14+
15+
16+
17+
同时从Socket缓冲区里拷贝一些offset和length到网络协议引擎里去,但是这个offset和length的量很少,几乎可以忽略
18+
19+
20+
21+
只要2次切换,2次拷贝,就可以了
22+
23+
24+
25+
kafka、tomcat,都是用的零拷贝技术,rocketmq用的是mmap技术,mmap还是要多2次切换和1次拷贝的,在Java代码中如何进行mmap和零拷贝,大家可以去看一看网上的一些资料
26+
27+
28+
29+
用户态和内核态,用户态空间,内核态空间

docs/03/images/106/01.png

85.1 KB
Loading

docs/03/images/106/02.png

76.2 KB
Loading

docs/03/images/107/01.png

86.6 KB
Loading

docs/03/images/108/01.png

83.7 KB
Loading

0 commit comments

Comments
 (0)