(该图转自http://bbs.chinaunix.net/thread-2018659-2-1.html,作者Aryang)
下面对各部分进行概述。
Linux进程的线性地址空间(进程虚拟地址空间分布),0~3G是User地址空间,3~4G是Kernel地址空间。(适用于ARM、X86等,mips按0~2G,2~3G划分)
关于线性地址布局,此图未说明部分:
1.紧接着内核数据区向上是mem_map全局page数组。
2. kernel启动地址并非是0xC0000000,而是PAGE_OFFSET+TEXT_OFFSET(0x8000),而在这32k大小的空间存放着内核一级页表数组swapper_pg_dir(每一项一级页表4个字节,映射1M内存,4G空间共4K项一级页表,占用16k内存),页表在启动阶段setup_arch->paging_init中调用prepare_page_table()和map_lowmem()进行初始化和映射,将在另一篇详细叙述。
3.内核动态加载驱动模块so将被load到紧接着MODULES_VADDR~0xC0000000的16M空间。
小于896M的物理内存直接映射到内核3G~3G+896M线性地址空间内,VA=PA-PHYS_OFFSET+PAGE_OFFSET
大约896M的物理内存通过建立各级页表的方式映射到高端映射区。
每个task在起task_struct内都有一个指针mm指向mm_struct,其控制着该task的所有内存信息,其成员mmap指向vma区链表,pgd指向页全局目录位置。
切换任务时要为下一个task装载其pgd到C2(X86为cr3)。
在cpu寻址时,mmu将虚拟地址按照2级映射方式,映射到对应物理叶匡的地址偏移上去。
而在内存管理上,系统启动阶段使用的是Bootmem,它是以简单的bitmap方式(0:未使用,1:已用)。
系统启动后buddy接手内存管理(以页为单位),分为两种情况,per_cpu_pageset和free_area,前者用来分配单个页(在Linux物理内存描述三个层级中有详细描述),后者用来分配多页的情况,11个链表管理页块的大小从2^1~2^11,每个链表中又分为不同迁移类型的子链表。
#define MIGRATE_UNMOVABLE 0
#define MIGRATE_RECLAIMABLE 1
#define MIGRATE_MOVABLE 2
#define MIGRATE_PCPTYPES 3 /* the number of types on the pcp lists */
#define MIGRATE_RESERVE 3
#define MIGRATE_ISOLATE 4 /* can't allocate from here */
MIGRATE_TYPES是为了有效解决内存碎片问题而引入的,将可移动(用户空间申请的内存可以重新映射)、不可移动(内核空间通常申请的)、可回收(文件映射)等组织在不同的链表中,申请内存时按照不同情况在各自类型的链表中释放,如果所在类型内存不足,会fallback到其他类型链表继续分配。在系统初始化完成时全部内存划分给MOVABLE类型链表,当有其他类型需求时再从MOVABLE中释放生成。
slab分配器是面向对象的分配机制,其建立在buddy基础之上,细节待另一篇详述。
框架
(该图转自bullbat的博客,强烈推荐该博客http://blog.csdn.net/bullbat/article/details/7166140)