内存管理和文件系统总会交织在一起,所以我们今天聊的内容和文件系统有关。
上一节的struct page结构体中,我们看到了一个成员struct address_space*。很明显是用于建立page和address_space的关联。
它是代表某个地址空间吗?实际上不是的,它是用于建立文件结点(struct inode)到内存页面(struct page)的映射,其实就是每个file都有这么一个结构,将文件系统中这个file对应的数据与这个file对应的内存绑定到一起;
与之对应,address_space_operations 就是用来操作该文件映射到内存的页面,比如把内存中的修改写回文件、从文件中读入数据到页面缓冲等。
file结构体和inode结构体中都有一个address_space结构体指针,实际上,file->f_mapping是从对应inode->i_mapping而来,inode->i_mapping->a_ops是由对应的文件系统类型在生成这个inode时赋予的。inode用于唯一地表征磁盘上的一个文件,而file可以共享同一个inode,例如硬链接便是如此,所以inode才是文件的根。
备注:关于什么是inode可以看文件系统分析章节。
下面这张图展示了文件和内存的映射体系:
struct address_space {
struct inode *host;
struct xarray i_pages;
struct rw_semaphore invalidate_lock;
gfp_t gfp_mask;
atomic_t i_mmap_writable;
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
/* number of thp, only for non-shmem files */
atomic_t nr_thps;
#endif
struct rb_root_cached i_mmap;
unsigned long nrpages;
pgoff_t writeback_index;
const struct address_space_operations *a_ops;
unsigned long flags;
struct rw_semaphore i_mmap_rwsem;
errseq_t wb_err;
spinlock_t private_lock;
struct list_head private_list;
void *private_data;
} __attribute__((aligned(sizeof(long)))) __randomize_layout;
-
host指针指向该address_space所属的inode
-
i_pages管理了该文件所映射的page
xarray是一种高效的基数树数据结构,用于管理大量的索引项。它在Linux内核中被广泛用于管理页面、文件索引等场景。
xarray的设计目标是提供高效的查找和操作性能,并且具有较低的内存开销。它通过将索引项组织成一棵基数树(Radix Tree)来实现这些目标。基数树是一种多叉树,每个节点的子节点数量可以根据需要动态增长。
在struct address_space结构体中,i_pages成员使用了xarray,用于管理地址空间中页面的索引。页面可以根据其页号作为索引项插入到xarray中,这样就可以快速地进行页面的查找、插入和删除操作。通过xarray,可以有效地管理大量页面,并且能够提供高性能的访问和操作。
- struct rb_root_cached i_mmap; 管理文件所映射的虚拟地址空间vma。
mmap.c:
static int vma_link(struct mm_struct *mm, struct vm_area_struct *vma)
{
VMA_ITERATOR(vmi, mm, 0);
struct address_space *mapping = NULL;
vma_iter_config(&vmi, vma->vm_start, vma->vm_end);
if (vma_iter_prealloc(&vmi, vma))
return -ENOMEM;
vma_start_write(vma);
vma_iter_store(&vmi, vma);
if (vma->vm_file) {
mapping = vma->vm_file->f_mapping;
i_mmap_lock_write(mapping);
__vma_link_file(vma, mapping);
i_mmap_unlock_write(mapping);
}
mm->map_count++;
validate_mm(mm);
return 0;
}
static void __vma_link_file(struct vm_area_struct *vma,
struct address_space *mapping)
{
if (vma_is_shared_maywrite(vma))
mapping_allow_writable(mapping);
flush_dcache_mmap_lock(mapping);
vma_interval_tree_insert(vma, &mapping->i_mmap);
flush_dcache_mmap_unlock(mapping);
}
vm_area_struct 用于描述进程的一段虚拟地址空间。