上一节描述了如何进行页级内存的分配与释放管理,可以比较有效地完成以页大小为最小单位(粒度)的内存分配和回收工作,这样可以很好地与分页机制配合在一起提供有效的分页管理。但在操作系统的实际运行过程中,还有很多对小于一页的任意大小内存的动态申请需求,则以页为最小单位就无法适应这类需求了。当然,我们可以直接把物理内存页管理器改为粒度为1字节的物理内存管理器,这主要存在两个问题:
- 由于页目录表和页表的大小是4KB,且一个页表项管理的内存空间大小也是4KB,故需要扩展新的函数和结构匹配对页表的管理支持,导致代码复杂;
- 即使采用上述4种内存分配算法,在支持任意大小的内存分配请求上,依然存在效率不高,有外碎片和内碎片等问题。
所以,一个更加合理的办法是在物理内存页管理器的基础之上建立一个支持任意大小的内存分配管理器,形成二级内存管理,满足高效支持任意大小的内存分配请求。这就对动态内存分配管理提出了新的挑战,即花尽量少的时间完成对内存的分配和回收,且保证能够产生的内外碎片尽量小。
proj6中参考Jeff Bonwick 为 Sun OS 操作系统首次引入的一种算法:slab算法。slab算法的基本思路有两个,一个是通过缓存实现“对象”重用,另一个是在一个连续页空间放同样类型的“对象”。
Jeff Bonwick在SUN OS内核中观察到内核在运行时会为有限的对象集(内核中各种常见的数据结构)分配大量内存,且对内核中这些数据结构进行初始化所需的时间超过了对其进行分配和释放所需的时间。因此他的结论是不应该将内存释放回一个全局的空闲内存池,而是将内存保持为针对特定目而初始化的状态。例如,如果内存被分配给了一个变量,那么只需在为此变量首次分配内存时执行一次变量初始化函数即可,当该变量被回收并进一步被后续分配时就不需要执行这个初始化函数,因为从上次释放和调用析构之后,它已经处于所需的状态中了。
在一个连续地址空间放同样类型的“对象”有助于快速查找和修改同样类型的“对象”,提高分配的时间效率,并减少碎片。
proj6基于proj5.2实现,主要在buddy物理内存页管理器的基础上,增加了一级任意大小内存分配管理,通过slab算法实现对小内存的简洁分配,为后续的运行时动态内存管理提供通用的内存申请和释放接口、在proj6中,可以了解到:
- slab算法的数据结构和具体实现;
- 小内存分配管理器与物理内存页管理器的接口和交互过程。
proj6
├── boot
│ └── ……
├── kern
│ ├── debug
│ │ └── ……
│ ├── driver
│ │ └── ……
│ ├── init
│ │ └── ……
│ ├── libs
│ │ └── ……
│ ├── mm
│ │ ├── ……
│ │ ├── memlayout.h
│ │ ├── slab.c
│ │ └── slab.h
│ ├── sync
│ │ └── ……
│ └── trap
│ └── ……
├── libs
│ └── ……
├── Makefile
└── ……
11 directories, 58 files
相对与proj5.2,proj6增加了slab.[ch]两个个文件,主要完成对slab内存管理算法的简单实现。
(THU.CST) os is loading ...
Special kernel symbols:
entry 0xc010002c (phys)
etext 0xc0109530 (phys)
edata 0xc0122aa0 (phys)
end 0xc0123cb8 (phys)
Kernel executable memory footprint: 144KB
memory managment: buddy_pmm_manager
e820map:
memory: 0009f400, [00000000, 0009f3ff], type = 1.
memory: 00000c00, [0009f400, 0009ffff], type = 2.
memory: 00010000, [000f0000, 000fffff], type = 2.
memory: 07efd000, [00100000, 07ffcfff], type = 1.
memory: 00003000, [07ffd000, 07ffffff], type = 2.
memory: 00040000, [fffc0000, ffffffff], type = 2.
check_alloc_page() succeeded!
check_pgdir() succeeded!
check_boot_pgdir() succeeded!
-------------------- BEGIN --------------------
PDE(0e0) c0000000-f8000000 38000000 urw
|-- PTE(38000) c0000000-f8000000 38000000 -rw
PDE(001) fac00000-fb000000 00400000 -rw
|-- PTE(000e0) faf00000-fafe0000 000e0000 urw
|-- PTE(00001) fafeb000-fafec000 00001000 -rw
--------------------- END ---------------------
check_slab() succeeded!
++ setup timer interrupts
100 ticks
100 ticks