目录
一、I/O调优的重要性
二、数据传输过程
1.磁盘到缓存区运动过程
2.零字节拷贝
三、虚拟内存
1.虚拟内存好处
2.内存页调度
四、文件I/O
1.分页技术操作系统执行I/O的过程
2.文件锁定
五、场景回顾
六、知识来源
一、I/O调优的重要性
I/O操作比内存中数据处理任务的时间更长,差别以数量级计。
第一列为处理一个数据单元所需要的时间,第二列为对该数据单元进行磁盘读写所需要的时间,第三列为每秒能处理的数据单元数,第四列为改变第一列第二列的值所能产生的数据吞吐率的提升。
图中相对说明:单位处理时间减半,仅能提高吞吐率2.2%,I/O时间减半,吞吐几乎翻倍。操作系统移动的是大块数据(缓存区),而java.io类喜欢操作小块数据--单个字节、几行文本,有了NIO就可以轻松把大块数据备份到直接使用的地方(ByteBuffer对象)。
备注:应该将I/O摆在性能调优的第一位,代码调试放在第二位。
二、数据传输过程
1.磁盘到缓存区运动过程
缓存区如何工作,是所有I/O的基础。输入/输出无非就是把数据移进或移出缓存区。
数据从磁盘向运行中的进程的内存区域移动的过程
1.进程使用read()系统调用,要求其缓存区被填满
2.内核随机向磁盘控制硬件发出命令,要求从磁盘读取数据。
3.磁盘控制器把数据直接写入内核缓存区,这一步通过DMA完成,无需主CPU协助。
4.一旦磁盘控制器把缓存区装满,内核即把数据从内核空间额临时缓存区拷贝到进程执行read()调用时指定的缓存区。
用户空间:即常规线程所在区域,非特权区域,不能直接访问设备,JVM即常规线程,驻守于用户空间。
内核空间:是操作系统所在的区域,特权区域,能与设备控制器进行通信,控制者用户区域空间进程的运行状态等。所有的I/O都直接或者间接的通过内核空间。
进程一个系统调用,将一连串缓存区地址传递给操作系统,内核按顺序填充或者排干若干缓冲区,读的时候可以发散到多个用户缓存区,写的时候可以从多个用户缓存区把数据汇聚起来。
备注:内核试图对数据进行高速缓存或者预读取,所以进程所需要数据可能已经在内核空间里了,已在内核空间的只需要拷贝该数据即可。如果不在内核空间,则进程被挂起,内核将数据读到内存。
2.零字节拷贝
传统数据从文件传到套接字的路径
一般的将数据从文件传到套接字的路径:
1.操作系统将数据从磁盘读到内核空间的页缓存中
2.应用将数据从内核空间读到用户空间的缓存中
3.应用将用户空间数据写回内存空间的套接字缓存中
4.操作系统将数据从套接字缓存写到网卡缓存中,以便将数据经网络发出
这里有四次拷贝
零字节拷贝方式
拷贝路径
1.transferTo() 中文件内容被DMA引擎加载入内核buffer
2.数据不再被拷入socket buffer被取代的只有descriptor关于位置和长度的信息追加到socket buffer,DMA引擎将数据直接从内核buffer拷贝到网卡缓存。
如果使用sendfile(Java 为: FileChannel.transferTo api),两次拷贝可以被避免,允许操作系统将数据直接从页缓存发送到网络上。
DMA(Direct Memory Access,直接内存存取) 传输将数据从一个地址空间复制到另外一个地址空间。
三、虚拟内存
1.虚拟内存好处
1.一个或者多个的虚拟地址可以指向同一个物理内存地址
2.虚拟内存空间可大于实际可用的硬件内存
下图用户空间和内核空间地址映射到同一个物理内存地址
把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,可以省去内核与用户空间拷贝,前提条件为:
1.内核与用户缓存区必须使用相同的页对齐
2.缓冲区的大小需磁盘控制器块大小(通常512字节)的倍数
3.内存页的大小总是磁盘块大小的倍数(操作系统把内存地址空间划分为页)
4.虚拟和物理内存页的大小总是相同
2.内存页调度
为了支持寻址空间大于物理内存,虚拟内存需要分页(通常称为交换)。分页区即从物理内存置换出来,存储在磁盘上的内存页面。
进程A有5个页面,其中两个装入内存,其余存储于磁盘。CPU中包含内存管理单元(MMU),逻辑上位于CPU与物理内存之间,该设备包含虚拟地址向物理内存地址转换的映射信息。当不存在与该虚拟页形成有效映射物理内存页,MMU会向CPU提交一个页错误,内核验证页的有效性,内核会安排页面调入操作,把缺失的页面内容读回物理内存,通常此时别的页面会移除内存。
四、文件I/O
1.分页技术操作系统执行I/O的过程
当用户进程请求读取文件数据时,文件系统需要确定数据具体在磁盘什么位置,然后着手把相关磁盘扇区读进内存。采用分页技术的操作系统执行I/O的过程:
1.确定请求数据分布在文件系统的哪些页
2.在内核空间分配足够的内存页,以容纳文件系统页
3.在内存页与磁盘的文件系统页之间建立映射
4.为每个内存页产生页错误
5.虚拟内存系统俘获页错误,安排页面调入,从磁盘上读取页内容,使页有效
6.一旦页面调入操作完成,文件系统即对原始数据进行解析,取得所需文件内容或属性信息
备注:大多数操作系统假设进程会继续读取文件剩余部分,因而会预读额外的文件系统页。如果内存 争用情况不严重,这些文件系统页可能在相当长的时间内继续有效。这样的话,当稍后该文件又被 相同或不同的进程再次打开,可能根本无需访问磁盘。
2.文件锁定
文件锁定机制允许一个进程阻止其他进程存取某文件,或限制其存取方式。文件锁定有两种方式:共享的和独占的。多个共享锁可同时对同一文件区域发生作用;独占锁则不同,它要求相关区域不能有其他锁定在起作用。共享锁和独占锁的经典应用:共享锁和独占锁的经典应用,是控制最初用于读取的共享文件的更新。某个进程要读取文件, 会先取得该文件或该文件部分区域的共享锁。第二个希望读取相同文件区域的进程也会请求共享锁。两个进程可以并行读取,互不影响。假如有第三个进程要更新该文件,它会请求独占锁。该进程会处于阻滞状态,直到既有锁定(共享的、独占的)全部解除。一旦给予独占锁,其他共享锁的读取进程会处于阻滞状态,直到独占锁解除。
五、场景回顾
在RocketMQ创建commitLog文件预热时,写入1G大小的假值(0)的意义。
使用mmap()内存分配时,只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存。当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常,进而进入内核空间分配物理内存、更新进程缓存表,最后返回用户空间,恢复进程运行。如果没有这些假值,系统不会实际分配物理内存,防止在写入消息时发生缺页异常。
RoecketMQ存储--映射文件预热【源码笔记】
六、知识来源
本文整理自《Java NIO》第一章