内存管理-page初始化,分配与回收

来源:互联网 发布:网络传播与策划 刘芸 编辑:程序博客网 时间:2024/05/10 19:26

内存管理-page初始化,分配与回收
物理页面不一定就是物理内存页面,也可能是盘上物理页面
内核把叶作为内存管理的基本单位,物理内存和虚拟内存都分成大小相等的页(4K)

在计算机中运行的程序,其代码、数据和堆栈的总量可以超过实际内存的大小,操作系统只将当前正在使用的程序和数据块保留在内存中,而将内存容纳不了的信息保留在磁盘上。必要时,操作系统负责在磁盘和内存之间交换(怎么确定不用)程序和数据块

mm/init.c的作用:
内存布局:
内核被装载在2M(_text)开始处,结束_etext,紧接着是内核数据至_end
第0页用于保存BIOS检测的硬件信息
0x000a0000 to 0x000fffff保存BIOS例程,即640K到1M
ffff0000 BIOS rom 的首地址
machine_specific_memory_setup( ):内存状况表
检查
----------------------------------------------------------------------------物理
page_size
page_offset:0xC0000000
PMD_SHIFT 22   
内存管理单元(MMU,管理内存把虚拟地址转换为线性地址的硬件)以页为单位进行处理
内核用struct page---include/linux/mm.h结构的代表每个物理页,即每个物理页一个(一段连续地址)
全局指针mem_map指向page数组,在数组里分成三个区

建立块(在到分区)
pglist_date:include/linux/mmzone.h
typedef struct pglist_data {
    zone_t node_zones[MAX_NR_ZONES];指向三个zone分区
    zonelist_t node_zonelists[GFP_ZONEMASK+1];分配策略
        int nr_zones;  // Number of zones in the node
        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 pglist_data *node_next;
} pg_data_t;
分区:内核为了管理叶而进行的一种逻辑分区struct zone(linux/mmzone.h)
由于外围器件很多,速度多不同,基本上每个空间都有分区,因为同一分区内访问速度相同,即介质均匀,所以在zone之上有个节点即pglist,每个pglist再有分区
zone_DMA(<16)DMA只能访问内存的前16M
 zone_NORMAL(16-896)
zone_HIGHMEM(>896)
typedef struct zone_struct {
    spinlock_t        lock;
    unsigned long       offset;该区在全局页表中的起始页号
    unsigned long        free_pages;
    unsigned long        pages_min, pages_low, pages_high;
    int            need_balance;
    free_area_t        free_area[MAX_ORDER];一组空闲区间队列,管理散的物理页面成块,保持伙伴系统的连续长度页面块  空闲,活跃
    struct pglist_data    *zone_pgdat;
    struct page            *zone_mem_map;
    unsigned long        zone_start_paddr;
    unsigned long        zone_start_mapnr;
    char            *name;
    unsigned long        size;
} zone_t;
typedef struct zonelist_struct {
    zone_t * zones [MAX_NR_ZONES+1]; // 指针数组,各个元素指向对应管理区,零终止,分配页面时尝试不同的管理区
} zonelist_t;

物理内存管理器负责管理系统中宝贵的物理内存资源。利用位图可记录内存单元的使用情况,利用链表则可以分别记录已分配的内存单元和空闲的内存单元
struct free_area_struct {
    struct page *next;     物理页块结构page的哈希头中指针
    struct page *prev;
    unsigned int * map;   分配情况位图
}free_area_t;构成伙伴系统,管理的最小内存块的大小是1页(4K),最大内存块的大小是512页(2M)或2048页)8M
-----------------------------------------------------这些都是物理空间管理
The Zoned Page Frame Allocator
请求调页:

malloc分配虚存结构vma_area_struct
空闲连入struct zone_struct-> free_area
获得页:mm/page_alloc.c get_free_pages(特殊要求,指数)调用alloc_pages()检查是否超出最大调用_alloc_pages()进行具体分配,使用计数加1,page_address()返回page->virture。
交换:swap_info_struct include/linux/swap.h
用户空间页面:普通段,mmap映射,共享内存
页回收:free_pages()

内核交换守护进程kswapd:当物理内存缺乏的时候,尝试释放某些物理页。保证系统有足够的空闲页,使内存管理系统能有效地运行
?tlb和cache基址内核自动实现
内核缓冲区管理器:
什么时使用cache:看具体代码谁调用
cache:一个cache管理一组大小固定的内存块,kmem_cache_s描述,系统中的所有cache结构通过其中的指针(c_nextp)连成一个环型链表。cache中的内存块来自一到多个slab
cache数据结构中有三个指针,,完全活动的slab(所有对象均在使用)在前、
                            部分活动的slab居中
                            空闲slab排在最后
高速缓存与slab的关系:
分配一个slab到cache:
释放一个slab从cache:
分配一个slab对象:
释放一个slab对象:
slab:一个slab是来自物理内存管理器的一到多个物理页。每个slab都用一个kmem_slab_s数据结构描述。属于同一个cache的所有slab用其中的s_nextp和s_prevp连成一个双向链表
并不丢弃已分配的对象,提高效率,应该是保存在cache的不同状态队列中
slab用于减少伙伴分配算法的调用,进程创建的很频繁,但所占空间大致相同
小区间的分配,减少碎片

slab管理的对象:kmem_bufctl_s


第14章进程地址空间
每一个进程都有自己的虚拟地址空间,所有进程共享内核空间(c0000000-ffffffff)
define page_offset 0xc0000000
内核编辑阶段,在arch/i386/kernel/head.S startup_32( )创建了一个临时全局页表,存放在变量swapper_pg_dir,位置在内核数据_end后面第一个页
最终页表转换:arch/i386/mm---pagetable_init( )注意针对实际不同大小物理内存的处理。

对于系统空间而言:给定一个虚拟地址X,其物理地址便为X+page_offset
内核运行在物理寻址模式,不需要页表
每个内存有进程要遵守的属性,可读可写可执行。访问不在有效范围内、或以不正确的方式访问,内核会结束该进程,返回段错误。

内存描述符:mm_struct—linux/sched.h该结构描述了一个进程的地址空间的所有信息,包括其页目录、内存区域结构的链表和树、LDT(segments)、以及各种数据在其虚拟地址空间中的起止位置、以及一些统计信息
创建和删除进程的地址空间:
分配内存描述符:fork()调用copy_mm()复制父进程的current->mm内存描述符
子进程通过fork.c中allocte_mm()宏从mm_cachep slab缓存中分配得到
如果想父子进程共享内存,调用clone()时社标志CLONE_VM,即线程,内核不再调用allocte_mm().仅仅做copy_mm().
销毁内存描述符:内核调用exit_mm()函数

每一个进程都拥有自己的页表(页目录+页表)。内核创建,处理页表项
一级页表浪费存储空间 ,linux采用三级页表
每个虚拟地址都为四个字段,三个页编号和一个偏移量

    第一级页表的基地址存好CR3,注意进程切换时它的值要变
    asm volatile(“mov %0,%%cr3”::”r”(_pa(next->pgd)))

include/asm-i386/pgtable-2level.h
宏PGD_SHIFT(22)表示线性地址中一级页表的起始偏移,即22-32高十位,
即PGD得指针数为2^10即1024个(每个指针4,PGD表4K),pgtable.h中PGDIR_SIZE代表PGD中每项指向2^22个空间
用第一级页编号PGD查第一级页表(此进程第一级页表其它项为0),得到第二级页表的基地址;
用第二级页编号PMD查第二级页表,得到第三级页表的基地址;
用第三级页编号查第三级页表PTE,得到物理页的基地址;
页表项pte_t的高20位看做物理页面的序号,低12位用于状态信息和访问权限,但是并不是定义在PTE中,另定义pgprot_t(include/asm-i386/page.h)具体定义pgtable.h
页表项的获得pgtable-2level.h,
present: 1==pte_present()相应页已被装入主存Accessed:交换时使用Dirty:Read/Write:User/Supervisor:PCD and PWT:Page Size :Global
将物理页的基地址加上偏移量,就得到了该虚拟地址对应的物理地址。
之后利用地址的方式:
映射过程由MMU完成
include/asm/pgtable.h
在Intel处理器的pgtable.h文件中,定义一组宏(内核能处理特定进程的页表。这样,内核就不需要知道页表条目的具体结构和组织情况)。understanding2.5
相应表项为0,那么pte_none()返回1。
pte_clear()清一个表项,set_pte()将表项设定为一个特定值
pte_user()读user状态  ptr_wrprotect()设读写状态
还有页操作和页分配函数

每个进程都有局部段描述表LDT和任务状态段TSS,两个表的起始地址都作为全局描述表GDT的一项。GDT最大能容忍8192项。内核基本上用的都是GDT.arch/i386/kernel/heads.s
(GDT的每位解释P41-情景分析)
CS,DS的定义include/asm-i386/segment.h 映像processor/h define start_thread

转换过程:缺页中断,产生异常,异常处理函数do_page_fault()arch/i386/mm/fault.c

32位地址意味着4G的虚拟地址空间
用户空间--虚拟内存区域:vm_area_struct(linux/mm.h)指定虚拟地址空间内连续的一个内存范围对同一块区域的处理有着相同的规则。我们需要用虚存空间的哪些部分。
实际使用的内存区域:cat /proc/任一项/maps
虚拟内存管理:
操作内存区域:判断进程地址空间内存是否满足某些条件
Find_vma()查找一个addr所在的区域。给定虚拟地址,找到对应的结构
创建虚拟结构体后insert_vm_struct()将其插入mm_struct的队列或AVL树中
Mmap()通过系统调用获得do_mmap()的功能(文件映射到内存,按指针方式便可操作)
do_mmap()创建一个新的线性空间,建立映射。include/linux/mm.h如果创建的空间与原来的空间相连则合成一个
Munmap()从特定的地址空间中删除指定的地址空间
请求调页:
写时复制:

typedef struct page {
    struct list_head list;        /* ->mapping has some page lists. */
    struct address_space *mapping;    /* The inode (or ...) we belong to. */
    unsigned long index;        /* Our offset within mapping. */
    struct page *next_hash;        /* Next page sharing our hash bucket in
                       the pagecache hash table. */
    atomic_t count;            /* Usage count, see below. */
    unsigned long flags;        /* atomic flags, some possibly
                       updated asynchronously */
    struct list_head lru;        /* Pageout list, eg. active_list;
                       protected by pagemap_lru_lock !! */
    wait_queue_head_t wait;        /* Page locked?  Stand in line... */
    struct page **pprev_hash;    /* Complement to *next_hash. */
    struct buffer_head * buffers;    /* Buffer maps us to a disk block. */
    void *virtual;            /* Kernel virtual address (NULL if
                       not kmapped, ie. highmem) */
    struct zone_struct *zone;    /* Memory zone we are in. */
} mem_map_t;

struct kmem_cache_s {
/* 1) each alloc & free */
    /* full, partial first, then free链表的双向指针 */
    struct list_head    slabs_full;同一个cache中不同状态的slab各自组成一个链表
    struct list_head    slabs_partial;
    struct list_head    slabs_free;
    unsigned int        objsize;        // Size of the objects included in the cache
    unsigned int         flags;    /* constant flags */
    unsigned int        num;    /* num of objs per slab */
    spinlock_t        spinlock;
#ifdef CONFIG_SMP
    unsigned int        batchcount; Chunk size for local cache refill or emptying
#endif

/* 2) slab additions /removals */
    /* order of pgs per slab (2^n) */
    unsigned int        gfporder;

    /* force GFP flags, e.g. GFP_DMA */
    unsigned int        gfpflags;//分配页面时传递给伙伴函数的参数

    size_t            colour;        /* cache colouring range */
    unsigned int        colour_off;    /* colour offset */
    unsigned int        colour_next;    /* cache colouring */
    kmem_cache_t        *slabp_cache;  //通用slab_cache的指针
    unsigned int        growing;
    unsigned int        dflags;        /* dynamic flags */

    /* constructor func */
    void (*ctor)(void *, kmem_cache_t *, unsigned long);

    /* de-constructor func */
    void (*dtor)(void *, kmem_cache_t *, unsigned long);

    unsigned long        failures;

/* 3) cache creation/removal */
    char            name[CACHE_NAMELEN]; //cache的name
    struct list_head    next;    //cache的双向链表指针
#ifdef CONFIG_SMP
/* 4) per-cpu data */
    cpucache_t        *cpudata[NR_CPUS];
#endif
#if STATS
    unsigned long        num_active;
    unsigned long        num_allocations;
    unsigned long        high_mark;
    unsigned long        grown;
    unsigned long        reaped;
    unsigned long         errors;
#ifdef CONFIG_SMP
    atomic_t        allochit;
    atomic_t        allocmiss;
    atomic_t        freehit;
    atomic_t        freemiss;
#endif
#endif
};

typedef struct slab_s {
    struct list_head    list;//指向上面结构体前三个变量的指针
    unsigned long        colouroff;//slab中第一个对象的偏移
    void            *s_mem;        //第一个分配对象的地址
    unsigned int        inuse;        /* num of objs active in slab */
    kmem_bufctl_t        free;    //Index of next free object in the slab, or BUFCTL_END if there are no free objects left
} slab_t;

对象:
typedef unsigned int kmem_bufctl_t
#define slab_bufctl(slabp)     ((kmem_bufctl_t *)(((slab_t*)slabp)+1))

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 丰声用户不存在怎么办 微信用户不存在怎么办 社保卡忘记密码怎么办 斗鱼忘记邮箱怎么办 手机看斗鱼怎么办办卡 微博昵称受限怎么办 被新浪屏蔽了怎么办 微博qq忘记密码怎么办 三星盖乐世忘记密码怎么办 三盖乐世4忘记密码怎么办 密码锁钥匙丢了怎么办 商标是tm标怎么办 环保合格证掉了怎么办 车子合格证丢了怎么办 大牙附近肿了怎么办 办好营业执照后怎么办商标 商标十年到期了怎么办 公司注册了商标怎么办 2018年属兔不适合结婚怎么办 交通事故对方没钱赔怎么办 滴滴快车出车祸怎么办 交警法院扣车怎么办 肇事逃逸没钱赔怎么办 撞死人无力赔偿怎么办 宝贝咳嗽很厉害怎么办 开车有人别车怎么办 判刑罚金交不起怎么办 罚金太多交不起怎么办 判决书上的罚金怎么办 刑事罚款不交怎么办 缎面的鞋脏了怎么办 夏天脸上长粉刺怎么办 鬃狮不吃东西怎么办 买二手车的车牌怎么办 榴莲打开了肉没熟怎么办 gmat的prep做完怎么办 电脑没ip地址怎么办 电脑ip地址缺失怎么办 cos还原不了人物怎么办 执行局抓人十五天不放人怎么办 笔记本电脑键盘按键错乱怎么办