PageCache
为了提升IO的效率,计算机的整体各处都是存在页缓存的概念的。
作为应用进程来说,一般都会存在以4k为单位的缓存页,也就是buffer,可以想象java中的bufferreader等。。
接着,作为系统内核来说,它内部也存在页缓存(pagecache)。
最后,对于硬盘的驱动本身,也存在缓冲区。
因此可以想象,读取一份文件,会从磁盘驱动本身开始,每次读到缓冲区大小的内容才会向上级返回,这样大大提高IO的效率。
而除了多级缓存之外,我们也不难想到,IO是个很慢的操作,如果一直让CPU去频繁的跟进IO的操作,那系统效率就太低了!
因此在早期的时候,就已经出现了一个协处理DMA,它来从CPU的手中分担IO的操作,由它专门来进行对磁盘内容读取到内核的操作,而只需要最终的时候发起中断信号让CPU将内核缓冲区来自磁盘已经填满的内容一次刷洗给用户空间的程序。
上面说的,是针对读取IO操作,pagecache的一个作用凸现。
而实际上,一个进程本身也本质就是一个二进制的落在磁盘上的文件,因此也会涉及到读入用户程序的一个过程,此时也依旧是操作系统先将内容读取到pagecache进行缓存。那这样有什么好处呢?当多个进程同时试图访问一处文件的时候,不需要消耗多份的文件内存,去共享这个缓存就好了。
在linux中包装成fd文件描述符,实际它们指向的是同一份pagecache,只不过各自文件描述符的各自偏移量不同,这样才能保证进程之间对文件的读取互不影响。
说白了,pagecache就是内核维护的一个抽象中间层,使得用户空间直接访问的是这个中间层,而不是实时和磁盘打交道,从而提高效率。
既然是内核维护的,就意味着占用了一定的内存空间,那占用了多少呢?是不是也会有什么RLU的淘汰机制?
且既然是缓存,就会有数据一致性的问题,比如修改了缓存,那磁盘空间的文件也能实时或者延时的被同步一致吗?会不会丢失修改?
接下来看一下内核pagecache的一些刷写策略:
vi /etc/sysctl.conf
可以添加如下项:
#pagecache分配内存大于内核可用内存的百分比阈值,超过此值内核将会后台触发将脏页刷写到磁盘(开启线程,程序不阻塞)
vm.dirty_background_ratio = 0
vm.dirty_background_bytes = 1048576
#pagecache分配内存大于内核可用内存的百分比阈值,超过此值内核将会前台阻塞触发将脏页刷写到磁盘(程序阻塞)
vm.dirty_ratio = 0
vm.dirty_bytes = 1048576
#写1,代表1/100秒,会触发一次pagecache脏页刷写磁盘
vm.dirty_writeback_centisecs = 5000
#脏页的生命周期可以存活多久
vm.dirty_expire_centisecs = 30000
注:
程序刚申请分配的pagecahe一开始就是脏页,需要被刷写过磁盘后才不是脏页,后续这些非脏页会随着LRU淘汰策略被淘汰。
反过来说,当内存不足时,对pagecache进行LRU淘汰时,如果被淘汰的页是一个脏页,此时即使还没达到内核刷写pagecache的阈值,也会先把脏页刷写到磁盘持久化后再进行淘汰。
因此,会产生脏页的操作:
1、新分配的pagecahe
2、对pagecache进行过了修改
可以通过pcstat(需要预装go环境)来查看pagecache:
pcstat out.txt
可以查看到这个文件的大小是多大(Size),实际总页数是多少(Pages),被cached到内核中的pagecache有多少页(Cached),缓存的百分比是多少(Percent)