前言

I/O缓存与内存映射
一般来说我们的CPU速度是比较快然而磁盘速度明显是低于CPU的,所以引入了缓存来解决这种速度上的差异,计算中的缓存无处不在,如上图所示,缓存就是为了缓和两个模块之间速率的冲突,也就是说我们去读写磁盘的时候可以将磁盘上的数据先放到缓存中,我们CPU去读写硬盘数据的时候不会去直接读取硬盘的数据而去缓存中读写,在应用层也存在缓存,比如我们在接受串口数据的时候可以开辟一个环形缓冲区来缓存串口的数据,这是在嵌入式系统设备中常有的做法,其目的都是为了更高效的运行

页高速缓存

I/O缓存与内存映射
页缓存是操作系统层面的概念,其英文名为Page Cache,它是以页为管理单位的,简单说就是我们内核中的一个缓冲区,我们知道磁盘的最小读写单位是一个扇区为512字节,尽管我们在应用层只读取10字节大小,在操作系统层面会根据文件描述符在磁盘中找到对应的inode节点,返回一个扇区的大小或者一个固定字节的大小到我们的内核缓冲区(物理内存ddr上)里面,我们调用read,write时候就会去内核缓冲区读写数据,提升了IO的性能,在一定程度上分离了应用程序空间和物理设备,减少了IO读盘次数
I/O缓存与内存映射
如上图所示是页缓存的读写流程,对于页缓存来说我们这个write写到页缓存之后,这个修改掉的页就叫脏页,后面调用fsync(fd)的时候才把脏页刷新到磁盘中,测试如下
1.向文件中写入数据
I/O缓存与内存映射
2.脏页情况
I/O缓存与内存映射
3.调用sync,再查看脏页情况
I/O缓存与内存映射
I/O缓存与内存映射

页高速缓冲2

对于应用开发者来说我们虽然可以通过标准的系统调用接口进行文件的I/O,但是如果我们对底层页缓存有一个更好的了解可以帮助我们写出更高效的程序,下面我们来说说页缓存在内核中是如何实现的

I/O缓存与内存映射
如图所示,右边为物理内存内存布局示意图,它会以页为单位进行划分,每个物理页都用一个struct page类型的结构体来表示,然后将每个结构体放在它对应内存空间区域的数组里面,在linux内核会将物理内存划分成三个区域,第一个区域是DMA区域,为连续的物理空间,第二个区域就是我们经常使用的内存,操作系统启动后的镜像就放在这一块,应用程序的加载地址等,第三个区域存放一些映射,设备和文件的映射等.
I/O缓存与内存映射
在LINUX内核为了防止内存碎片化(ZONE_NORMAL),它使用了一个伙伴算法,如右图所示,Linux内核将相同的页大小组织成一个链表,1表示是表示这是4K内存块的链表,2表示这是8K内存块的链表…以此类推,用户每次去申请内存时就会去链表中取出内存,当释放后又放回链表中,这样就避免了内存碎片和空洞的产生,而已2个4K的内存可以合并成一个8K的内存块放入8k内存块链表中
I/O缓存与内存映射
在内核空间我们经常使用kmalloc,vmalloc,用户空间使用malloc申请内存时都是通过我们的伙伴系统去申请,当我们释放内存块的时候也回通过伙伴系统加入到原来的位置,kmalloc经常去slab里面去申请内存,因为伙伴系统的最小单位是4k,所以当我们申请10字节空间时伙伴系统会返回4k到slab缓存供使用,当释放时也会返回到slab缓存中
I/O缓存与内存映射
LINUX中每个进程都有一个4G的虚拟地址空间,0-3G是用户空间(独享),3-4G是内核空间(共享),当我们在内核空间使用kmalloc申请物理内存时,在线性映射区对应的物理内存分配空间,返回的虚拟地址和物理地址对应起来,如图中的两个绿色区域,同样的当我们用户空间去申请一片堆内存的时候也是如此,如图中的红色区域,当有多个进程时申请堆内存时可能会返回相同的虚拟地址,但是它们会映射到不同的物理内存,所以一个进程才有3G的用户空间

相关文章: