Linux内核文件cache管理机制介绍

1 操作系统和文件Cache管理

操作系统是计算机上最为重要的软件,他负责管理计算机的各种硬件资源,并将这些物理资源抽象成各种接口供上层应用使用。所以从程序的角度看,操作系统就好比是一个虚拟机,这个虚拟机不提供各种硬件资源的具体细节,而仅仅提供进程、文件、地址空间以及进程间通信等等逻辑概念。

对于存储设备上的数据,操作系统向应用程序提供的逻辑概念就是“文件”。应用程序要存储或访问数据时,只需要向这个文件读或者写入内容即可,而文件与物理存储设备的交互则由操作系统提供。

2 文件Cache的地位和作用

文件Cache是文件在数据内存中的副本,因此文件Cache管理与内存管理系统和文件系统相关:一方面文件Cache作为物理内存的一部分,需要参与内存的分配与回收。另一方面,cache中的数据来源于存储设备上的文件,需要通过文件系统与存储设备进行读写交互。

如图1所示,具体的文件系统,比如ext2/ext2,jfs,ntfs等等负责在文件Cache和存储设备之间交换数据,位于文件系统之上的虚拟文件系统VFS负责在应用程序和文件Cache之间通过read和write等接口交换数据。内存管理系统负责内存的分配与回收,同时虚拟内存管理系统(VMM)则允许应用程序和文件Cache之间通过memory map的方式交换数据。

3 文件Cache的相关数据结构

在Linux的实现中,文件Cache分为两个层面,page cache和buffer cache。每一个page cache包含若干个buffer cache。VFS负责page cache和用户空间的数据交换;内存管理系统负责page cache的分配与回收,同时在使用memory map访问时负责建立映射。而具体的文件系统则与buffer cache进行交互,将外围存储设备上的数据和buffer cache之间进行交换。

在Linux内核中,文件的每个数据块最多只能对应一个page cache项,其通过两个数据结构管理,一个radix tree另一个就是双向链表。radix tree是一个搜索树,linux利用这个数据结构来通过文件内偏移快速丁文cache项。下图是radix
tree的⼀一个⽰示意图,该radix tree
的分叉为4(22),树⾼高为4,⽤用来快速定位8位⽂文件内偏移。Linux(2.6.7)内核中的分叉为
64(26),树⾼高为6(64位系统)或者11(32位系统),⽤用来快速定位32
位或者64
位偏移,radix tree中的每⼀一个叶⼦子节点指向⽂文件内相应偏移所对应的Cache项。

另⼀一个数据结构是双向链表,Linux内核为每⼀一⽚片物理内存区域(zone)维护active_list和inactive_list两个双向链表,这两个list主要⽤用来实现物理内存的回收。这两个链表上除了⽂文件Cache之外,还包括其它匿名(Anonymous)内存,如进程堆栈等。 

4 文件Cache的预读和替换

Linux内核中文件预读算法的具体过程是这样的:对于每个文件的第一个读请求,系统读入所请求的页面并读入紧随其后的少数几个页面(不少于一个页面,通常是三个页面),这时的预读称为同步预读。对于第二次读请求,如果所读页面不在Cache中,即不在前次预读的group中,则表明文件访问不是顺序访问,系统继续采用同步预读;如果所读页面在Cache中,则表明前次预读命中,操作系统把预读group扩大一倍,并让底层文件系统读入group中剩下尚不在Cache中的文件数据块,这时的预读称为异步预读。无论第二次读请求是否命中,系统都要更新当前预读group的大小。此外,系统中定义了一个window,它包括前一次预读的group和本次预读的group。任何接下来的读请求都会处于两种情况之一:第一种情况是所请求的页面处于预读window中,这时继续进行异步预读并更新相应的window和group;第二种情况是所请求的页面处于预读window之外,这时系统就要进行同步预读并重置相应的window和group。图5是Linux内核预读机制的一个示意图,其中a是某次读操作之前的情况,b是读操作所请求页面不在window中的情况,而c是读操作所请求页面在window中的情况。

Linux内核中文件Cache替换的具体过程是这样的:刚刚分配的Cache项链入到inactive_list头部,并将其状态设置为active,当内存不够需要回收Cache时,系统首先从尾部开始反向扫描active_list并将状态不是referenced的项链入到inactive_list的头部,然后系统反向扫描inactive_list,如果所扫描的项的处于合适的状态就回收该项,直到回收了足够数目的Cache项。

5 文件Cache相关API及其实现

Linux内核中与文件Cache操作相关的API有很多,按其使用方式可以分成两类:一类是以拷贝方式操作的相关接口, 如read/write/sendfile等,其中sendfile在2.6系列的内核中已经不再支持;另一类是以地址映射方式操作的相关接口,如mmap等。

第一种类型的API在不同文件的Cache之间或者Cache与应用程序所提供的用户空间buffer之间拷贝数据,其实现原理如图7所示。

第二种类型的API将Cache项映射到用户空间,使得应用程序可以像使用内存指针一样访问文件,Memory map访问Cache的方式在内核中是采用请求页面机制实现的,其工作过程如图8所示。

首先,应用程序调用mmap(图中1),陷入到内核中后调用do_mmap_pgoff(图中2)。该函数从应用程序的地址空间中分配一段区域作为映射的内存地址,并使用一个VMA(vm_area_struct)结构代表该区域,之后就返回到应用程序(图中3)。当应用程序访问mmap所返回的地址指针时(图中4),由于虚实映射尚未建立,会触发缺页中断(图中5)。之后系统会调用缺页中断处理函数(图中6),在缺页中断处理函数中,内核通过相应区域的VMA结构判断出该区域属于文件映射,于是调用具体文件系统的接口读入相应的Page
Cache项(图中7、8、9),并填写相应的虚实映射表。经过这些步骤之后,应用程序就可以正常访问相应的内存区域了。

时间: 2024-11-24 13:18:44

Linux内核文件cache管理机制介绍的相关文章

Ubuntu清理老旧无用Linux内核文件教程

  在 Ubuntu 环境中,我们有很多种方式可以升级内核. Ubuntu 桌面版本可以在每天自动更新时升级内核,Ubuntu Server 可以使用 unattended-upgrade 自动更新的方式来升级内核,当然还可以使用 apt-get 或 aptitude 来同时兼容 Ubuntu 桌面或服务器版本. 随着使用时间的推移,被替换下来的老旧内核版本将会占用一定的磁盘空间而造成浪费.每个内核映像文件和相关的模块文件/header(头文件)会占用大约 200 – 400 M 的磁盘空间,如

【内核】几个重要的linux内核文件【转】

转自:http://www.cnblogs.com/lcw/p/3159394.html Preface      当用户编译一个linux内核代码后,会产生几个文件:vmlinz.initrd.img, 以及System.map,如果配置过grub引导管理器程序,会在/boot目录下看到这几个文件.     vmlinuz      vmlinuz是可引导的.压缩的内核文件.    该文件包含了一个最小功能的内核,在PC上通常是先执行vmlinuz,之后加载initrd.img文件,最后加载根

Linux内核中的信号机制的介绍

  应用程序发送信号时,主要通过kill进行.注意:不要被"kill"迷惑,它并不是发送SIGKILL信号专用函数.这个函数主要通过系统调用sys_kill()进入内核,它接收两个参数: 第一个参数为目标进程id,kill()可以向进程(或进程组),线程(轻权线程)发送信号,因此pid有以下几种情况: ● pid>0:目标进程(可能是轻权进程)由pid指定. ● pid=0:信号被发送到当前进程组中的每一个进程. ● pid=-1:信号被发送到任何一个进程,init进程(PID=

Linux中文件查看/编辑命令介绍

cat 命令介绍 cat 命令的原含义为连接(concatenate), 用于连接多个文件内容并输出到标准输出流中(标准输出流默认为屏幕).实际运用过程中,我们常使用它来显示文件内容.如果您熟悉MS-DOS 下的type 命令,相信不难掌握cat 命令.该命令的常用示例如下: cat file1.txt 显示 file1.txt 文件的内容: cat file1.txt file2.txt 显示 file1.txt 和file2.txt 文件的内容: cat -n file1.txt 显示 fi

Linux内核实践之tasklet机制【转】

转自:http://blog.csdn.net/bullbat/article/details/7423321 版权声明:本文为博主原创文章,未经博主允许不得转载. 作者:bullbat          源代码分析与原理部分参见前面的文章linux中断延迟之tasklet,这里我们看看他的使用,和前面介绍的工作队列.等待队列等相同,声明采用两种方法,我们在init函数中直接采用tasklet_schedule(&my_tasklet);调度,内核中的实现很简单: void __tasklet_

linux内核md源代码解读 六 介绍raid10阵列的运行

raid10的run函数与raid5的run函数最大区别在于setup_conf,那就直接深入核心: 3540 static struct r10conf *setup_conf(struct mddev *mddev) 3541 { 3542 struct r10conf *conf = NULL; 3543 int err = -EINVAL; 3544 struct geom geo; 3545 int copies; 3546 3547 copies = setup_geo(&geo,

linux内核md源代码解读 五 介绍raidd5阵列的运行

如果看懂了raid1阵列的run函数,那么看raid5阵列run就非常轻松了,因为两者要做的事情都是大同小异. raid5的run函数很长,但很大一部分跟创建运行是没有关系的,特别是有一段跟reshape相关的,大多数系统都不关注该功能,因此可以直接跳过.经过删减之后的run函数如下: 5307 static int run(struct mddev *mddev) 5308 { 5309 struct r5conf *conf; 5310 int working_disks = 0; 5311

大话Linux内核中锁机制之RCU、大内核锁

大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linux内核中锁机制>系列博文进行了总结,并提出关于目前Linux内核中提供的锁机制的一些基本使用观点. 十.RCU机制 本节将讨论另一种重要锁机制:RCU锁机制.首先我们从概念上理解下什么叫RCU,其中读(Read):读者不需要获得任何锁就可访问RCU保护的临界

Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】

原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938390.html   在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段,      下面我就顺这代码逐个函数的解释,但是这