《深入理解LINUX内存管理》学习笔记(二) 0.01版

初始化mem_map

mem_map是一个struct page的数组,管理着系统中所有的物理内存页面。在系统启动的过程中,创建和分配mem_map的内存区域。UMA体系结构中,free_area_init()函数在系统唯一的struct node对象contig_page_data中node_mem_map成员赋值给全局的mem_map变量。调用的关系图:

《深入理解LINUX内存管理》学习笔记(二) 0.01版_第1张图片

主要的核心函数free_area_init_core(),为node的初始化过程分配本地的lmem_map(node->node_mem_map)。数组的内存在boot memory 分配的alloc_bootmem_node()函数分配.在UMA体系结构中,这个新分配的lmem_map成为全局的mem_map. 对于NUMA体系,lmem_map赋值给每一个node的node_mem_map成员,而这个情况下mem_map就被简单的赋值为PAGE_OFFSET(有兴趣理解NUMA体系结构的可以阅读英文原版,了解更多信息)。UMA体系中,node中的各个zone的zone_mem_map就指向mem_map中的某些元素作为zone所管理的第一个page的地址。

中间插段广告

《深入理解LINUX内存管理》学习笔记(二) 0.01版_第2张图片

Linux文化T恤,淘宝销售,有兴趣的可以购买。

淘宝店面地址:

http://list.taobao.com/browse/search_auction.htm?user=b0ccaa7bfdc57fdec4594501767832b6&commend=all

Page

系统中的每个物理页面用struct page数据结构对象来表示,并且跟踪page使用的状态:(省略了一些特定平台用到的成员)

struct page {
        unsigned long flags;         
        atomic_t _count;
        union {
                atomic_t _mapcount;
                unsigned int inuse;
        };
        union {
            struct {
                unsigned long private;
                struct address_space *mapping;
            };
            struct kmem_cache *slab;    /* SLUB: Pointer to slab */
            struct page *first_page;    /* Compound tail pages */
        };
        union {
                pgoff_t index;          /* Our offset within mapping. */
                void *freelist;         /* SLUB: freelist req. slab lock */
        };
        struct list_head lru;       

#if defined
(WANT_PAGE_VIRTUAL
) 
        void *virtual;
#endif         
};

union {
                atomic_t _mapcount;
                unsigned int inuse;
        }: 和页表转换有关的PTE链,下面章节将描述。

index:这个成员根据page的使用的目的有2种可能的含义。第一种情况:如果page是file mapping的一部分,它指明在文件中的偏移。如果page是交换缓存,则它指明在address_space所声明的对象:swapper_space(交换地址空间)中的偏移。第二种情况:如果这个page是一个特殊的进程将要释放的一个page块,则这是一个将要释放的page块的序列值,这个值在__free_page_ok()函数中设置。

mapping: 当文件或设备需要内存映射,文件或设备的inode对象有一个address_space类型的成员。如果page属于这个文件或设备,mapping将指向inode中这个成员。如果page不属于任何文件或设备,但是 mapping被设置了,则mapping指向了一个address_space类型的swapper_space对象,则page用于管理交换地址空间(swap address space)了。

lru: page交换调度策略使用。page可能被调度到active_list或者inactive_list队列里。就是使用lru这个list_head。

private:这个保存了一些和mapping(文件mapping到内存)有关的一些特定的信息。如果page是一个buffer page,则它就保存了一个指向buffer_head的指针。

virtual: 不再用于将high memory的映射到ZONE_NORMAL区域的作用了,除了一些其他的体系结构会用到外。

count: page的访问计数,当为0是,说明page是空闲的,当大于0的时候,说明page被一个或多个进程真正使用或者kernel用于在等待I/O。

flags:  page状态的标志信息。kernel代码里定义了大量的宏用于设置,清楚,检测flag成员中的各个位所表示的page状态信息。特别提示一下,SetPageUptodate(),它需要调用一个和体系结构有关的函数:arch_set_page_uptodate().

《深入理解LINUX内存管理》学习笔记(二) 0.01版_第3张图片

《深入理解LINUX内存管理》学习笔记(二) 0.01版_第4张图片

映射页面到zone(Mapping page to zones)

在2.4.18内核之前,struct page数据结构中有一个zone的成员,后来证明这样做会无谓的浪费大量的内存空间,因为系统中会有大量的page对象,所以以后版本的page中不在有这样的成员了,而是有一个索引表示,这个索引保存在flag成员中的某些位段中,这个索引占用8个位。2.6.19版本的kernel系统中建立了一个全局的zone数组:

struct zone
 *zone_table
[1 << ZONETABLE_SHIFT
] __read_mostly
;
EXPORT_SYMBOL
(zone_table
);
EXPORT_SYMBOL宏的作用,是让zone_table能够被其他载入的模块使用。free_area_init_core()函数对node里的所有page做初始化。
zone_table[nid * MAX_NR_ZONES + j] = zone; //对zone_table做初始化。
nid是node ID。 j是zone的索引。
对每个page调用set_page_zone()初始化page中的zone的索引值(在page->flag中)。
set_page_zone(page, nid * MAX_NR_ZONES + j);
 

但是2.6.20后就不用这一套了,mm/sparse.c文件中做了一套管理系统。新的方法将多个page组成section来管理。 

这里略微描述一下,有兴趣的,可以详细阅读sparse.c的源代码。kernel将所有的page分成多个section管理,对于x86平台,有64个section,每个section管理着(1<<26)个或(1<<30)个(对于支持PAE的情况下)内存区域。

以下是几个主要的define:

include/asm-x86/sparsemem_32.h:

#ifdef CONFIG_X86_PAE
#define SECTION_SIZE_BITS       30
#define MAX_PHYSADDR_BITS       36
#define MAX_PHYSMEM_BITS        36
#else
#define SECTION_SIZE_BITS       26
#define MAX_PHYSADDR_BITS       32
#define MAX_PHYSMEM_BITS        32
#endifinclude/linux/mmzone.h:#define SECTIONS_SHIFT          (MAX_PHYSMEM_BITS - SECTION_SIZE_BITS)#define NR_MEM_SECTIONS         (1UL << SECTIONS_SHIFT)#ifdef CONFIG_SPARSEMEM_EXTREME
#define SECTIONS_PER_ROOT       (PAGE_SIZE / sizeof (struct mem_section))
#else
#define SECTIONS_PER_ROOT       1
#endif#define SECTION_NR_TO_ROOT(sec) ((sec) / SECTIONS_PER_ROOT)
#define NR_SECTION_ROOTS        (NR_MEM_SECTIONS / SECTIONS_PER_ROOT)
#define SECTION_ROOT_MASK       (SECTIONS_PER_ROOT - 1)

首先声明了一个全局的mem_section的全局数组。
struct mem_section *mem_section[NR_SECTION_ROOTS];

调用sparse_add_one_section()函数,分配mem_section,并且初始化。

 

你可能感兴趣的:(数据结构,linux,struct,list,table,buffer)