您的位置:首页 > 运维架构 > Linux

linux内核2.6.36 页框回收之缓存部分入门知识(一)

2011-06-06 12:55 369 查看

1.页框回收概述

本文分析的是Linux2.6.36版本下的页框回收,主要着重于缓存部分。
操作系统管理内存中的物理页面,同时也担任着内存分配的职责。应用程序可以通过内存分配函数向操作系统申请物理页面;在使用完这些物理页面之后,应用程序可以通过相应的内存释放函数释放这些物理页面。但是,对于内存中的某些物理页面来说,页面的使用者并不会主动释放它们,如果这些物理页面一直被占用而得不到释放,那么无论计算机上可用的物理内存有多少,物理内存迟早都有被用完的时候。所以,对于无法被主动释放的物理页面来说,操作系统就需要提供相应的功能去释放它们,Linux 中提供页面回收算法这样一种机制进行页面回收。

一般来说,用于页缓存的物理页面无法被页面的使用者主动释放,因为它们不知道这些页面何时应该被释放。Linux 中页缓存存在的最大好处就是可以让程序从缓存中快速获取数据,从而提升系统的性能。在系统负载不重的情况下,Linux 操作系统会分配较多的物理页面用于页缓存,从而提高程序的运行效率;但是在系统负载较重的情况下,Linux 操作系统就可能会适当回收用于缓存的页面,并减少用于缓存的页面的分配,从而满足系统中优先级更高的内存分配请求。对于用户进程来说,Linux 操作系统可以在它需要的时候为它分配物理内存,但是当用户进程不再需要这些物理页面的时候,如果用户进程不主动释放占用的页面,Linux 操作系统也不会强制用户进程去释放这些物理页面。基于上述这些情况,当内存中可用的物理页面越来越少,并最终导致内存的使用捉襟见肘的时候,为了保证系统的顺利运行,Linux 操作系统就会根据一定的算法去回收那些长期被占用并且没有得到有效使用的物理页面。

由操作系统内核本身使用的物理页面不在 Linux 操作系统进行页面回收的考虑范围之内,这是因为与用户进程相比,内核不需要占用非常多的内存,回收内核占用的物理页面会显著增加内核代码的复杂性,潜在收益非常低。

2.PFRA回收的缓存类型

PFRA回收的缓存类型包括存有磁盘文件数据且在页高速缓存中的页、块设备缓存区页、某些磁盘高速缓存的页(如索引节点高速缓存,目录项高速缓存)、slab高速缓存、交换高速缓存。下面对上述几种缓存名词进行分析。

2.1索引节点和目录项高速缓存

虚拟文件系统(VFS)是一个内核软件抽象层,用来处理与unix标准文件系统相关的所有系统调用,为各种文件系统提供了一个通用的接口。VFS引入了一个通用的文件模型,这个模型能够表示所有支持的文件系统。通用文件模型(一个软件结构)由下列对象类型组成:
超级块对象(superblock object):存放已安装文件系统的有关信息。对基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件系统控制块(filesystem control block)。
索引节点对象(inode object):存放关于具体文件的一般信息。比如文件访问权限、大小、拥有者、创建时间等。比如对于目录(directory)/bin/vi, bin和vi都是文件,inode object对应每个文件。对基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件控制块(file control block)。每个索引节点对象都有一个索引节点号,这个节点号唯一地标识文件系统中的文件。为了加速访问时间,我们把最近经常访问的索引节点对象保留在slab高速缓存中。Inode结构如下:
struct
inode {
struct hlist_node i_hash; /* hash list */
struct list_head i_list; /* list of inodes */
struct list_head i_sb_list; /* list of superblocks */
struct list_head i_dentry; /* list of dentries */
unsigned long i_ino; /* inode number */
atomic_t i_count; /* reference counter */
unsigned int i_nlink; /* number of hard links */
uid_t i_uid; /* user id of owner */
gid_t i_gid; /* group id of owner */
kdev_t i_rdev; /* real device node */
u64 i_version; /* versioning number */
loff_t i_size; /* file size in bytes */
seqcount_t i_size_seqcount; /* serializer for i_size */
struct timespec i_atime; /* last access time */
struct timespec i_mtime; /* last modify time */
struct timespec i_ctime; /* last change time */
unsigned int i_blkbits; /* block size in bits */
blkcnt_t i_blocks; /* file size in blocks */
unsigned short i_bytes; /* bytes consumed */
umode_t i_mode; /* access permissions */
spinlock_t i_lock; /* spinlock */
struct rw_semaphore i_alloc_sem; /* nests
inside of i_sem */
const struct inode_operations *i_op; /*
inode ops table */
const struct file_operations *i_fop; /*
default inode ops */
struct super_block *i_sb; /* associated
superblock */
struct file_lock *i_flock; /* file lock list
*/
struct address_space *i_mapping; /*
associated mapping */
struct address_space i_data; /* mapping for
device */
struct dquot *i_dquot[MAXQUOTAS]; /* disk
quotas for inode */
struct list_head i_devices; /* list of block
devices */
union {
struct pipe_inode_info *i_pipe; /* pipe
information */
struct
block_device *i_bdev; /* block device driver */
struct
cdev *i_cdev; /* character device driver */
};
unsigned
long i_state; /* state flags */
unsigned
long dirtied_when; /* first dirtying time */
unsigned
int i_flags; /* filesystem flags */
atomic_t
i_writecount; /* count of writers */
void
*i_security; /* security module */
void
*i_private; /* fs private pointer */
};

文件对象(file object):存放打开文件与进程之间进行交互的有关信息。这类信息仅当进程访问文件期间存放在内核内存中。
目录项对象(dentry object):对于目录(directory),比如/bin/vi,VFS经常需要解析路径中的每个部分是否有效,因此我们引入了directory entry(dentry)表示目录中的每个部分,如/,bin和vi,使得解析路径的高效运行。这个对象只是在运行过程中,需要解析路径才出现的,在磁盘上并不存在。Dentry object结构如下:
struct dentry
{
atomic_t d_count; /* usage count */
unsigned int d_flags; /* dentry flags */
spinlock_t d_lock; /* per-dentry lock */
int d_mounted; /* is this a mount point? */
struct inode *d_inode; /* associated inode
*/
struct hlist_node d_hash; /* list of hash
table entries */
struct dentry *d_parent; /* dentry object of
parent */
struct qstr d_name; /* dentry name */
struct list_head d_lru; /* unused list */
union {
struct list_head d_child; /* list of
dentries within */
struct rcu_head d_rcu; /* RCU locking */
} d_u;
struct list_head d_subdirs; /*
subdirectories */
struct list_head d_alias; /* list of alias
inodes */
unsigned long d_time; /* revalidate time */
struct dentry_operations *d_op; /* dentry
operations table */
struct super_block *d_sb; /* superblock of
file */
void *d_fsdata; /* filesystem-specific data
*/
unsigned char d_iname[DNAME_INLINE_LEN_MIN];
/* short name */
};
一个有效的目录项对象由三种状态:used, unused, negative。
一个used的目录项对象和一个有效的inode对象(d_inode)对应,表示有一个或多个用户在使用这个对象(d_count),这个对象不能被回收。
一个unused的目录项对象和一个有效的inode对象(d_inode)对应,但是VFS当前并未使用(d_count = 0),这时,目录项对象保存在缓存中,并没有被销毁,以便将来文件路径查找操作时能够使用。当然,这个对象是可以回收的。
一个negative的目录项对象并没有和一个inode对象对应(d_inode = NULL),但是这个目录项对象还是保留在缓存中,主要用于下列这种情形:一个守护进程持续打开并读取一个并不存在的配置文件,这调用了open()系统调用函数,这个函数需要查找文件路径,验证文件不存在,只要发现dentry object状态为negative,立刻知道文件不存在了。当然,这个对象是可以回收的,因为没有用户在使用。
下图表示进程和文件怎么进行交互。



在上述图中,文件对象对应了一个文件路径,这个文件路径通过一个hash表映射到目录项的对象的链表,目录项高速缓存存储着最近使用的目录项,每个目录项和索引节点高速缓存中的每个索引节点对应。

2.2页高速缓存

页高速缓存是一种对完整的数据页进行操作的磁盘高速缓存。在linux2.6.36中,磁盘缓存只有页高速缓存(page cache),不包括缓冲区页(buffer cache),buffer cache已经包含在page cache中。在绝大多数情况下,内核读写磁盘时都引用页高速缓存。新页被追加到页高速缓存以满足用户态进程的读请求。如果页不在高速缓存中,新页就被加到高速缓存中,然后用从磁盘读出的数据填充它。如果内存有足够多的空闲空间,就让该页在高速缓冲中长期保留,使其他进程再使用该页时不再访问磁盘。同样,在把一页数据写到块设备之前,内核首先检查对应的页是否已经在高速缓存中,如果不在,就要先在其中增加一个新项,并用要写入到磁盘中的数据填充该项。I/O数据的传送并不是马上开始,而是要延迟几秒之后才对磁盘进行更新,从而使进程有机会对要写入磁盘的数据做进一步的修改(也就是内核执行延迟的写操作)。

页高速缓存中的每个页所包含的数据肯定属于某个文件。这个文件(即文件的索引节点)成为页的所有者。一个页中包含的磁盘块在物理上不一定相邻,所以不能用设备号和块号来识别它,取而代之的是,通过页的所有者和所有者数据中的索引(通常是一个索引节点和在相应文件中的偏移量)来识别高速缓存中的页。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: