受到xv6的启发,我也想给我襁褓之中的“操作系统”赋予内存管理功能。一个完善的内存管理需要考虑很多failover,缓存、tlb、page replacement等诸多功能,导致实际中使用的内存管理系统非常复杂。我决定我的内存管理从设计开始就极度简化,只实现最基础的功能。
设计
把所有内存组织成一个链表kmem,用kalloc和kfree管理物理内存分配,粒度为1MB。128MB内存被分成128块
1 | kalloc 分配连续1MB的物理内存 |
在内核启动后,打开mmu,进入虚拟内存布局。
- 整个128MB物理内存被映射到0xC000 0000的高地址,作为内核空间
- 0x2000 0000的外设IO地址仍然被映射到原来的位置,这样在启动mmu后,mmio操作不需要改变base address
- 低1MB的虚拟地址控制作为用户空间,为了简化,不打开缓存、tlb功能,不实现内存页置换算法。
这里用三个内核函数实现
1 | setupkvm 在初始化的时候,建立虚拟内存映射 |
实现
首先,需要修改kernel.ld 链接文件,把text首地址改成
1 | . = 0xC0008000; |
保证跳转指令中的地址匹配虚拟地址。
也可以用linker script的at指令来改变实际load address。我们在一开始entry.S中设置好栈就开启mmu,因此整个image可以都load到0xC0008000,而不需要具体控制某一个section的 load address。
接下来,通过setupkvm函数,修改位于0x0000-0x4000的一级页表,映射虚拟内存布局。最后通过操作cp15协处理器打开mmu
1 | mcr p15, 0, 1, c3, 0 |
设置domain 0位client模式(user空间页描述符为domain 0)
1 | mcr p15, 0, 0, c2, c0 |
设置ttbr为0,一级页表位于0x0000-0x4000,共4GB/1MB = 4096个页描述符,16KB
1 | mcr p15, 0, 1, c1, c0, 0 |
启动mmu
最后我们来验证一下,mmu是否开启成功了
1 | // mmu test, cp should be high address |
把pc寄存器的值打印出来,如果大于0xC000 0000,则证明mmu开启成功
source code
1 | git clone -b memory https://github.com/996refuse/emperorOS.git |
赏
使用支付宝打赏
使用微信打赏
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章