目录
一、两种管理物理内存方法
UMA计算机(一致内存访问),SMP系统中的每个处理器访问各个内存区都是同样快。
NUMA计算机(非一致内存访问),系统的各个CPU都有本地内存,可支持特别快速的访问。各个处理器之间通过总线连接起来,以支持对其它CPU的本地内存的访问。
二、结点 pglist_data
node_zonelists指定了备用结点及其内存域的列表,以便在当前结点没有可用空间时,在备用结点分配内存。在build_all_zonelists中初始化。
为结点2的ZONE_HIGHMEM分配备用结点的情况,如下图:
三、内存域 zone
lowmen_reserve数组分别为各种内存域指定了若干页,用于一些无论如何都不能失败的关键性内存分配。
冷热页帧,在分配内存页时,首先从热页帧列表中分配(除非指定从冷页帧列表中分配),如果没有,则从伙伴系统中分配若干页到该列表中。页面回收时,进入热页帧列表。回收的页帧大概率存在于CPU缓存中,每次先从热页帧列表分配,有助于CPU缓存命中。
四、页帧 page
五、页表
内核内存管理总是嘉定使用四级页表,IA-32体系结构只是用两级。
PGD、PUD、PMD、PTE、Offset
六、内核在内存中的布局
前4KiB是第一个页帧,一般会忽略,通常保留给BIOS使用。
接下来的640KiB原则上是可用的,但也不用于内核加载。其原因是,该区域之后紧邻的区域由系统保留,用于映射各种ROM。内核总是会装载到一个连续的内存区中,如果要从4KiB处作为起始位置来装载内核映像,则要求内核必须小于640KiB。
七、内核地址空间划分
八、bootmem分配器
用于伙伴系统启用之前,通过bit位记录各个页帧使用情况(低端内存域页帧,DMA和NORMAL)
九、伙伴系统
struct zone --> struct free_area free_area[MAX_ORDER] --> struct list_head free_list[MIGRATE_TYPES]
首先区分分配阶,再区分迁移类型(用于防止产生碎片)。
涉及页帧包含直接映射的所有物理页帧。
十、VMALLOC
使用 struct vm_struct 链表记录所分配的虚拟地址空间信息。该结构所用内存通过kmalloc分配。
主要用于分配高端内存域的页帧。
十一、持久内核映射
pkmap_count其中每个元素都对应于一个持久映射页,用于标记对应虚拟内存页是否已经映射。
struct page_address_map用于建立页帧和虚拟内存的映射关系。该结构存储于page_address_htable数组中,散列表。struct page_address_map中的链表元素用于建立溢出链表,以处理散列碰撞。散列函数是page_slot。
page_address函数可以根据指定的page获得对应的虚拟地址。对于高端内存域,使用上述散列表计算。对于直接映射的内存域,则通过偏移量直接计算。
十二、临时内核映射
对应于固定映射区。
相对于持久内核映射,该映射执行是原子的,在固定映射区域中,每个处理器都有一个对应的窗口,每个窗口中,每种映射类型都对应于一项。
十三、slab分配器
嵌入式系统可以使用slob,大型计算机使用slub
着色是为了给slab添加偏移量,否则,对于不同slab中,具有相同偏移的objects大概率会落到同一个cache line上,造成cache line的争用。
struct kmem_cache中的struct array_cache *array[NR_CPUS]用处类似于伙伴系统中使用的冷热页。在申请内存时,首先在array_cache中申请;申请不到时,再从slab中取若干objects放入该结构中;如果slab中也没有足够objects,再从伙伴系统中分配slab。
slab头部管理数据后的数组,每一项对应一个object,数组项纪录了下一个空闲的object便宜值。
slab系统初始化过程中也使用了slab系统,涉及了鸡生蛋蛋生鸡的问题,所以采用了分步初始化,在未完全初始化的时候,采用静态变量来赋值,在初始化过程中,逐渐将静态变量替换为通过slab动态分配的内存。
十四、进程虚拟内存
默认情况下,内存映射区向上增长,起始地址通常是1GiB处(TASK_UNMAPPED_BASE起始于0x4000000),那么意味着堆最多只有1GiBson控件可以使用,为避免此种情况,引入新的虚拟地址空间布局,如下图:
由于栈是有界的,因此安置内存映射的区域可以在栈末端的下方立即开始。
十五、内存映射 vm_area_struct
每个打开文件都表示为struct file的一个实例。该结构包含了一个指向地址空间对象struct address_space的指针。该对象时优先查找树的基础,而文件区间与其映射到地址空间之间的关联即通过优先树简历。此外,每个文件和块设备都表示为struct inode的一个实例。
对于非线性映射,页表项中存储了文件偏移量,以便在从后备设备中读取正确的内容进内存。
参考 《深入Linux内核架构》