首页 > 代码库 > Linux内存管理 (一) 内存组织

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 */
};