1. 实现互斥锁雏形
内核的各个功能组件后续都有申请释放内存的需求,所以需要实现内存管理。在实现内存管理之前我们需要先实现互斥锁,因为通过下面我们看出内存管理时通过链表实现的,操作链表时需要加锁,所以先实现简单的互斥锁。
由于全志D1时单核处理器,所以当一段代码执行时唯一可能打断的就是中断,那么互斥锁实现的原理就很简单了:
- 申请锁时先关中断,然后判断锁是否被别人获取,如果是则睡眠,不是则获取锁,最后打开中断。
- 释放锁时也是先关中断,然后尝试唤醒等锁的进程,最后打开中断。
void mutex_init(struct mutex *lock, char *name) { lock->locked = 0; lock->name = name; } void mutex_lock(struct mutex *lock) { int_disable(); if (lock->locked) { // TODO:添加睡眠调度代码 } lock->locked = 1; int_enable(); } void mutex_unlock(struct mutex *lock) { int_disable(); lock->locked = 0; // TODO:添加唤醒代码 int_enable(); }
2. 实现内核物理页管理
内核物理页管理参考xv6,首先对内核总共可使用内存做个限制,这里我选择为128MB。然后为了简单,约定每次只能申请一页内存也就是4KB,并且初始化时是将这所有空闲的4KB页面通过链表串起来, 每个物理页的开始是一个指针,指向下一个空闲页的地址,这样就将内存管理结构嵌在页面自身里面,达到节省空间和简单化的目的。
// 释放一页内存 void kfree(void *addr) { struct page* page; // 检查地址是否合法 if(((u64)addr % PAGE_SIZE) != 0 || (char*)addr < end || (u64)addr >= KERN_STOP) panic("kfree"); page = (struct page*)addr; // 将空闲页插入到链表头 mutex_lock(&kmem.lock); page->next = kmem.freelist; kmem.freelist = page; mutex_unlock(&kmem.lock); } // 申请一页内存 void *kalloc(void) { struct page* page; // 从链表头取出空闲页 mutex_lock(&kmem.lock); page = kmem.freelist; if (page) { kmem.freelist = page->next; } mutex_unlock(&kmem.lock); return (void *)page; } // 内核页管理初始化 void kmem_init(void) { char *p; char *pa_start = end; char *pa_end = (char *)KERN_STOP; mutex_init(&kmem.lock, "kmem"); // 初始化时将所有空闲内存构造为空闲链表 p = (char*)PAGE_ROUNDUP((u64)pa_start); for(; p + PAGE_SIZE < (char*)pa_end; p += PAGE_SIZE) { kfree(p); } }
评论