在上一部分,我们讲了动态内存分配器的原理是维护一个堆,而且是实现各种连续内存分配方法.
但是上一部分是直接通过引用了buddy_system_allocator来解决的问题.
那么对于内存分配算法有兴趣的我,还是决定看一下源码,总之人是咸鱼但是还是需要有梦想.
人生这么不顺,若是连梦想都没有了,可能当即就找不到活着的意义了吧.
获取buddy_system_allocator的源码
buddy_system_allocator也是rCore这个社区的项目.- cd ~/workspace
- git clone https://github.com/rcore-os/buddy_system_allocator.git
复制代码 从实用的角度开始看源码
为了起一个好头,还是从比较熟悉的部分看代码,思考代码是怎么组织的:
- buddy_system_allocator是怎么作为一个外部包被引用的?
- 上一部分我们调用了LockedHeap,那么这个类是怎么实现的,它依赖于什么?
LockedHeap
我们在源码中搜索LockedHeap,我们可以在lib.rs里找到它的实现.- pub struct LockedHeap<const ORDER: usize>(Mutex<Heap<ORDER>>);
复制代码 在看到这个定义的时候有一种似懂非懂的感觉,只能猜到LockedHeap是一个加了线程锁的大小为ORDER的Heap:
- 因为ORDER放在了中间,应该是和泛型有关系,但是这里又明确标注了usize说明ORDER是一个变量.
- 因为在结构体的实现中出现了()有点不知所云
元组结构体
查看Rust圣经,发现确实存在这种字段可以没名称的结构体.
这里又产生了一个新的疑问,如果字段可以没名称,那么怎么去访问结构体内容呢?
查阅Rust语言官方参考手册,可以看到:
Tuple structs are similar to regular structs, but its fields have no names. They are used like tuples, with deconstruction possible via let TupleStruct(x, y) = foo; syntax. For accessing individual variables, the same syntax is used as with regular tuples, namely foo.0, foo.1, etc, starting at zero.
通过数字来访问这些结构体内容.- // 假如存在TupleStruct这个结构体
- let foo = TupleStruct(1,2);
- // 可以通过这种方法来进行析构
- let TupleStruct(x, y) = foo;
- // 可以用数字访问
- let x = foo.0;
- let y = foo.1;
复制代码 值泛型
那么这里就需要查看参考书目-值泛型的内容尤其是它的示例.
最终得到结论:Rust是允许使用值的泛型的,这代表LockedHeap有一个和值相关的泛型参数.
在某些时候是很像C里边的#define ORDER 0x30000的.
但是事实上在Rust里是灵活了非常多的.
这和LockedHeap提供的两种获取示例的方法是相对应的:- impl<const ORDER: usize> LockedHeap<ORDER> {
- /// Creates an empty heap
- pub const fn new() -> Self {
- LockedHeap(Mutex::new(Heap::<ORDER>::new()))
- }
- /// Creates an empty heap
- pub const fn empty() -> Self {
- LockedHeap(Mutex::new(Heap::<ORDER>::new()))
- }
- }
复制代码 单看这里还看不出来,因为还套了一层Heap,要看Heap的获取实例的方法.
加互斥锁
理解了上边的语法,只需要理解GlobalAlloc这个trait对于LockedHeap的实现:- #[cfg(feature = "use_spin")]
- unsafe impl<const ORDER: usize> GlobalAlloc for LockedHeap<ORDER> {
- unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
- self.0
- .lock()
- .alloc(layout)
- .ok()
- .map_or(core::ptr::null_mut(), |allocation| allocation.as_ptr())
- }
- unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
- self.0.lock().dealloc(NonNull::new_unchecked(ptr), layout)
- }
- }
复制代码 实际上就是在Heap外边加了一个Mutex互斥锁,那么对于alloc和dealloc的实现,只需要经过互斥锁访问里边的Heap,然后访问Heap的alloc和dealloc方法.
Heap
定义
Heap实际上由一个长度为ORDER的list和user,allocated和total几个值组成.- pub struct Heap<const ORDER: usize> {
- // buddy system with max order of `ORDER - 1`
- free_list: [linked_list::LinkedList; ORDER],
- // statistics
- user: usize,
- allocated: usize,
- total: usize,
- }
复制代码 那么ORDER实际上就是const 值泛型了.
为什么在代码里不需要指定ORDER的值?
因为我们设置的包的版本为0.6,这个版本的包没用加入泛型参数,而是固定链表长度为32.
获取实例
查看Heap的new和empty方法:- impl<const ORDER: usize> Heap<ORDER> {
- /// Create an empty heap
- pub const fn new() -> Self {
- Heap {
- free_list: [linked_list::LinkedList::new(); ORDER],
- user: 0,
- allocated: 0,
- total: 0,
- }
- }
- /// Create an empty heap
- pub const fn empty() -> Self {
- Self::new()
- }
- }
复制代码 这里注意list是一个LinkedList类型,是一个链表.
设置堆范围
记得上一篇博客内容,我们是使用如下代码初始化的:- /// Initialize heap allocator
- pub fn init_heap() {
- unsafe {
- HEAP_ALLOCATOR
- .lock()
- .init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE);
- }
- }
复制代码 这里且不说HEAP_ALLOCATOR.lock()是怎么获取到Heap实例的.这里这句init确实是调用的Heap的init.
接下来我们看它的实现.
[code]impl Heap { ... ... /// Add a range of memory [start, end) to the heap pub unsafe fn add_to_heap(&mut self, mut start: usize, mut end: usize) { // avoid unaligned access on some platforms start = (start + size_of: ) - 1) & (!size_of: ) + 1); end &= !size_of: ) + 1; assert!(start |