Linux内存管理 (一) 内存组织
内存管理是内核最复杂同时也是最重要的一部。其特点在于非常需要处理器和内核之间的协作。
首先内存划分为结点,在内核中表示为pg_data_t,每个结点划分为内存域。
以下的所有数据结构或代码都做了不同程度的精减,一方面是为了保留相关代码,除去细枝末叶,另一方面是为了美观。
结点的数据结构为
<mmzone.h>
typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES]; /*内存结点所包含的内存域数组*/ struct zonelist node_zonelists[MAX_ZONELISTS]; /*指定了备用结点及其内存域列表*/ int nr_zones; /*内存域的数目*/ #ifdef CONFIG_FLAT_NODE_MEM_MAP struct page *node_mem_map; /*node_mem_map是指向page实例数组的指针,它包含了结点中所有内存域的页*/ #endif struct bootmem_data *bdata; /*bdata指定了用于自举分配器数据结构的实例*/ #ifdef CONFIG_MEMORY_HOTPLUG #endif unsigned long node_start_pfn; /*结点内第一个页帧的逻辑编号*/ unsigned long node_present_pages; /* 物理内存页的总数,不包括内存洞 */ unsigned long node_spanned_pages; /* 物理内存页的总长度,包括洞在内 */ int node_id; /*结点id*/
struct pglist_data *pgdata_next; /*下一个内存结点的指针*/ } pg_data_t;
内存域是由枚举来表示的,如下所示
<mmzone.h> enum zone_type { #ifdef CONFIG_ZONE_DMA ZONE_DMA,/*ZONE_DMA表示DMA区域。在IA-32机器上,一般的限制是0~16M。*/ #endif ZONE_NORMAL,/*ZONE_NORMAL表示可直接映射到内核段的普通内存域。在x86机器上为16M~896M.*/ #ifdef CONFIG_HIGHMEM ZONE_HIGHMEM,/* ZONE_HIGHMEM标记了超出内核段的物理内存。如在x86机器上,内存为4G,那么896M~4G就为高端内存。*/ #endif ZONE_MOVABLE, /* ZONE_MOVABLE表示的内存域属于伪内存域,主要用于防止内存碎片。*/ MAX_NR_ZONES/*MAX_NR_ZONES充当结束标记。用于内存域的迭代。*/ };
内存域的结构描述如下:
<mmzone.h>
struct zone {
/*
*pages_min, pages_low, pages_high是水印,用于面换出时的计算
*pages_high表示如果内存域中的空闲页多于此值,则内存域的状态是理想的
*pages_low表示如果内存域中的空闲页低于此值,则内核开始将页换出到硬盘
*pages_min表示如果内存域中的空闲页低于此值,则表示空闲页极少。此时页回收工作压力很大。
*/ unsigned long pages_min, pages_low, pages_high;
/*
*lowmen_reserve表示为各个内存域预留的页数目,用于不能失败的关键性内存分配
*/ unsigned long lowmem_reserve[MAX_NR_ZONES]; struct per_cpu_pageset pageset[NR_CPUS]; /* 每个CPU的冷热帧列表*/ spinlock_t lock; /*自旋锁*/ struct free_area free_area[MAX_ORDER];/*用于伙伴系统的数据结构*/ spinlock_t lru_lock; /*自旋锁*/ struct list_head active_list; /*活动页的集合,page实例*/ struct list_head inactive_list; /*不活动页的集合, page实例*/ unsigned long nr_scan_active;/*指定回收内存时需要扫描的活动页的数目*/ unsigned long nr_scan_inactive;/*指定回收内存时需要扫描的不活动页的数目*/ unsigned long pages_scanned; /* 指定了上一欠掏出一页以来,有多少页未能成功扫描*/ unsigned long flags; /* 内存域的当前状态 */ atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS]; /*内存的统计信息*/ struct pglist_data *zone_pgdat; /*指定pg_data_t实例*/ /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */ unsigned long zone_start_pfn; /*内存域的第一个页帧的索引*/ };
页帧的数据结构:
struct page { unsigned long flags; /* 页帧的标记*/ atomic_t _count; /* 使用记数 */ union { atomic_t _mapcount; /*内存管理子系统中映射的页表项计数,用于表示页是否已经映射,还用于限制逆向映射搜索*/ unsigned int inuse; /* 用于SLUB分配器:对象的数目*/ }; union { struct { unsigned long private; /* 由映射私有,不透明数据*/ struct address_space *mapping; /*映射所在的地址空间*/ }; struct kmem_cache *slab; /* 指向slub的指针 */ struct page *first_page; /* 用于复合页的尾页,指向首页 */ }; union { pgoff_t index; /* 在映射内的偏移量 */ void *freelist; /* SLUB: freelist req. slab lock */ }; struct list_head lru; /*用于在各种不同的鍡上维护该页*/ #if defined(WANT_PAGE_VIRTUAL) void *virtual; /*用于高端内存中的页。存储该页的虚拟地址*/ #endif /* WANT_PAGE_VIRTUAL */ };