用户名: 密码: 忘记密码? 注册

Linux内存管理的重要数据结构

作者:  时间: 2010-09-03

这里只列举几个比较麻烦的数据结构

struct list_head {
    struct list_head *next, *prev;
} //linux通用的双向链队列,下面多处用到,这里列出源代码

linux对内存再用页式管理,对于页,就有个数据结构page加以描述。在内核中有个全局量mem-map指向的是一个page的数组,整个数组 描述了整个物理内存,大家注意32位地址线,内存大小是2的32次方。由于页大小是4k也就是2的12次方。每4K的页就有一个page。那么2的32次 方共有2的20次方个页,则mem-map指向的数组大小就是2的20次方。其中明显对于页的物理地址,一定是4K的倍数,因为它大小是4K,那么这个数 组就有个对应,数组下标后面12个0(2进制)对应了页面的物理地址,他们在数值上是相等的。那么我们在page数据结构中就没有必要存贮它的物理地址。 page数据结构定义位置include/linux/mm.h

struct page{

struct list_head list;   //通过使用它进入下面的数据结构free_area_struct结构中的双向链队列

struct address_space * mapping;   //用于内存交换的数据结构

unsigned long index;//当页面进入交换文件后

struct page *next_hash; //自身的指针,这样就可以链接成一个链表

atomic t count; //用于页面交换的计数,若页面为空闲则为0,分配就赋值1,没建立或恢复一次映射就加1,断开映射就减一

unsigned long flags;//反应页面各种状态,例如活跃,不活跃脏,不活跃干净,空闲

struct list_head lru;

unsigned long age; //表示页面寿命

wait_queue_head_t wait;

struct page ** pprev_hash;

struct buffer_head * buffers;

void * virtual

struct zone_struct * zone; //指向所属的管理区

}

(对于每个项的说明,我会慢慢补上)

对内存,仅仅用page数据结构来描述肯定远远不够,对于整个内存,我们在此之上分了管理区的概念,每个管理区管理数个页面。这个数据结构是 zone_struct定义位置是在include/linux/mmzone.h

typedef struct free_area_struct {
    struct list_head free_list;   //linux 中通用的双向链队列
    unsigned int * map;
} free_area_t;

typedef struct zone_struct{

    spinlock_t        lock;

    unsigned long offset;  //表示该管理区在mem-map数组中,起始的页号

    unsigned long free pages;
    unsigned long inactive_clean_pages;
    unsigned long inactive_dirty_pages;
    unsigned pages_min, pages_low, pages_high;
   
    struct list_head inactive_clean_list;   //用于页面交换的队列,基于linux页面交换的机制。这里存贮的是不活动“干净”页面
    free_area_t free_area[MAX_ORDER]; 

//一组“空闲区间”队列,free_area_t定义在上面,其中空闲下标表示的是页面大小,例如:数组第一个元素0号,表示所有区间大小为2的 0次方的页面链接成的双向队列,1号表示所有2的1次方页面链接链接成的双向队列,2号表示所有2的2次方页面链接成的队列,其中要求是这些页面地址连续
   
    char * name;
    unsigned long size;
   
    struct pglist_data * zone_pgdat;   //用于指向它所属的存贮节点,及下面的数据结构
    unsigned  long  zone_start_paddr;
    unsigned  long    zone_start_mapnr;
    struct page * zone_mem_map;

} zone_t;

我们知道内存的地位并不是“平等的”,例如主内存和图形卡上的静态内存ram就不是“平等地位”,因此我们页面管理机制做了修正,管理区不是内存管理中最高层的概念,前述的page数组mem-map也不是全局,而是从属于具体的节点,在zone_struct 上面有了一层代表存贮节点的数据结构pglist_data,定义于include/linux/mmzone.h 中

typedef struct pglist_data {
    zone_t    node_zones[MAX_NR_ZONES];   //该节点最多的3个页面管理区,MAX_NR_ZONE值是3,在linux中分为3个管理区,也可能是2个ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM
    zonelist_t    node_zonelist[NR_GFPINDEX];   //具体说明见下面的数据结构
    struct page * node_mem_map;   //用于指向属于该存储节点的page数组
    unsigned long * valid_addr_bitmap;
    struct bootmem_data    * bdata;
    unsigned long node_start_paddr;
    unsigned long node_start_mapnr;
    unsigned long node_size;
    int    node_id;
    struct pglistdata * node_next;   //用于形成一个单链队列
}    pg_data_t;

typedef struct zonelist_struct {
    zone_t * zones [MAX_NR_ZONES+1]; //指针数组,指向每个具体的页面管理区
    int gfp_mask;
}    zonelist_t; //用于描述内存分配策略的数据结构,比如内存中最小适应法则的分配策略,我们就可以将区放入zones数组中,按照区内空闲页面从小到大的原则,但我们要实现这个法则,我们只需要从头到尾便利这个数组,那么找到第一个大于你要求的页面个数的区就是最小适应区

这些都是用于物理空间管理的,虚拟空间管理由下面的数据结构描述(我刚开始把虚拟空间弄成了虚拟内存,越看越糊涂,虚拟空间指的是,在linux 中,4G内存被分为2块,搞地址1G内存叫系统空间,所有进程共享,下面的3G便是虚拟空间,每个进程都占有虚拟空间3G,在进程自己看来是这样的,但实 际上,他远远没这么多内存用),vm_area_struct数据结构,定义于 include/linux/mm.h,进程虚存空间的描述,他被进一步组织到mm_struct中,它描述是地址连续的空间

struct vm_area_struct {
    struct mm_struct * vm_mm;   //下面说明
    unsigned long  vm_start;
    unsigned long vm_end;   //start和end决定了虚存空间,个人理解应该是mem-map表的下标,其中 start包含,end不包含,同时区间划分不单单是地址连续,还要保证权限的统一
   
    struct vm_area_struct * vm_next;   //将属于同一进程的虚存区间按照虚存地址高低链接起来
   
    pgrot t_vm_page_prot;
    unsigned long vm_flags;   //存贮该区间的权限
   
    short vm_avl_height;
    struct vm_area_struct * vm_avl_left;
    struct vm_area_struct * vm_avl_rigth; //用于生成 avl树,提高搜索效率
   
    struct vm_area_struct * vm_next_share;
    struct vm_area_struct ** vm_pprev_share;
   
    struct vm_operations_struct * vm_ops;   //下面说明
    unsigned long vm_pgoff;
   
    struct file * vm_file;
    unsigned long vm_raend;
    void * vm_private_data;   //这些属性都是用于记录页面与文件关系,具体情况具体分析
}

定义于include/linux/mm.h

struct vm_operations_struct {
    void (*open) (struct vm_area_struct * area);
    void (*close) (struct vm_area_struct * area);
    struct page * (*nopage)(struct vm_area_struct *area, unsigned long address, int write_access);
} //open, close,nopage用于虚存空间打开,关闭和建立印射

 

定义于include/linux/sched.h,进程所有的虚存空间的数据结构描述

struct mm_struct{
    struct vm_area_struct * mmap;   //建立虚存空间的单链队列
    struct vm_area_struct * mmap_avl;   //建立虚存空间的AVL树
    struct vm_area_struct * mmap_cache;   //最近一次使用的虚存空间,由于内存访问总是带有局部性,命中率有35%
    pgd_t * pgd;   //指向进程页面目录,在载入进程时候这个值会被载入到cr3寄存器中
    atomic_t mm_users;
    atomic_t mm_count;
    //这2个变量比较令人费解,mm_users记录是虚存空间的使用者,而mm_count记录的是mm_struct使用的计数。首先虚存空间是可以多 个进程使用的,比如如果父进程调用vfork创建子线程,此时2者使用的是同一个虚存空间。线程是没有自己的虚存空间。其次一个mm_struct对应一 个虚存,有几个使用者那么就有几个进程通过指针共享了这个mm_struct,按理说这2个计数应该是一个,不应该分成2个。大部分可能性2者是相同的。 但又特殊情况,比如内核线程是没有虚存空间,他是需要暂借调用者的虚存空间,只是虚存空间使用者和mm_struct使用计数就不统一
    int map_count;
    struct semaphore mmap_sem;   //用于进程的间的互斥访问
    spinlock_t page_table_lock;
   
    struct list_head mmlist;
   
    unsigned long start_code, end_code, start_data, end_data
    unsigned long start_brk, brk, start_stack;
    unsigned long arg_start, arg_end, env_start, env_end;
    unsigned long rss, total_vm, locked_vm;
    unsigned long def_flags;
    unsigned long cpu_vm_mask;
    unsigned long swap_cnt;
    unsigned long swap_address;
    mm_context_t context;
}

在操作系统中,有个进程控制块(PCB)的概念,具体到linux里面对应的数据结构是task_struct,它内部就有个mm_struct指针,mm_struct是整个用户空间的抽象。待续。。。