Linux内存管理中内存的组织及主要数据结构分析(pg_data_t&&page&&zone)
在讲解内核中用于组织内存的数据结构之前,考虑到术语不总是容易理解,所以先来看看几个概念。我们首先考虑NUMA系统,这样,在UMA系统上介绍这些概念就非常容易了。
下图给出内存划分的图示:

首先,内核划分为结点。每个结点关联到系统中的一个处理器,在内核中表示为pa_data_t的实例(稍后定义该数据结构)。各个结点又划分为内存域,是内存的进一步细分。例如,对可用于(ISA设备的)DMA操作的内存区是有限制的。只有钱16MB适用,还有一个高端内存区无法直接映射,在二者之间是通用的“普通”内存区。内核引入下列常量来枚举系统中的所有内存域:
struct page {unsigned long flags;//存储了体系结构无关的标志,用于描述页的属性atomic_t _count;//是一个使用计数,表示内核中应用该页的次数。在其值到达0时,内核就知道page实例当前不使用,因此可以删除;如果其值大于0,该实例绝不会从内存删除。union {atomic_t _mapcount;//内存管理子系统中映射的页表项计数,表示在页表中有多少项指向该页unsigned int inuse;//用于SLUB分配器,对象的数目};union { struct {unsigned long private;//是一个指向“私有”数据的指针,虚拟内存管理会忽略该数据。根据页的用途,可以用不用的方式使用该指针,大多数情况下它用于将页与缓冲区关联起来。struct address_space *mapping;//mapping默认情况下是指向address_space的,但如果使用技巧将其最低位置1,mapping就指向anon_vma对象 };#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS spinlock_t ptl;#endif struct kmem_cache *slab;//用于SLUB分配器,指向slab的指针 struct page *first_page;//用于复合页的尾页,指向首页};union {pgoff_t index;//在映射内的偏移量void *freelist;/* SLUB: freelist req. slab lock */};struct list_head lru;//是一个表头,用于在各种链表上维护该页,一遍将页按不用类别分组,最重要的类别是活动页和不活动页/* * On machines where all RAM is mapped into kernel address space, * we can simply calculate the virtual address. On machines with * highmem some memory is mapped into kernel virtual memory * dynamically, so we need a place to store that address. * Note that this field could be 16 bits on x86 ... ;) * * Architectures with slow multiplication can define * WANT_PAGE_VIRTUAL in asm/page.h */#if defined(WANT_PAGE_VIRTUAL)void *virtual;//用于高端内存区域中的页,换言之,即无法直接映射到内核内存中的页,virtual用于存储该页的虚拟地址。#endif /* WANT_PAGE_VIRTUAL */};上述结构中使用了大量的union结构,考虑一个例子:一个物理内存页能够通过多个地方的不同页表映射到虚拟地址空间,内核想要跟踪有多少地方映射了该页,为此,struct page中有一个计数器用于计算映射的数目。如果一页用于slab分配器(后面的博客会详细介绍),那么可以确保只有内核会使用该页,而不会有其它地方使用,因此映射计数信息就是多余的,因此内核可以重新解释该字段,用来表示该页被细分为多少个小的内存对象使用,联合体就很适用于该问题。
注:我只是一个内核的初学者,如果有哪些地方说的不对或是不准确,请指正;如果你有什么问题希望能提出来,一起分析讨论一下,以求共同进步。谢谢!