《30天自制操作系统》笔记(07)——内存管理
上一篇中处理掉了绝大部分与CPU配置相关的东西。本篇介绍内存管理的思路和算法。
现在想想,从软件工程师的角度看,CPU也只是一个软件而已:它的功能就是加载指令、执行指令和响应中断,而响应中断也是在加载指令、执行指令。就像火车沿着一条环形铁轨前进;当中断发生时,就好像铁轨岔口处变轨了,火车就顺着另一条轨迹走了;走完之后又绕回来重新开始。决定CPU是否变轨的,就是CPU里的特定寄存器。
这是题外话,就此为止。
什么是内存管理
假设内存大小是128MB,应用程序A暂时需要100KB,画面控制需要1.2MB……。
像这样,操作系统有时要分配一定大小的内存,用完后又要收回。因此,必须管理好哪些内存空闲可用,哪些正在被占用。这就是内存管理。
内存管理的两个任务,一是内存分配,一是内存释放。
如何获取内存容量
检查内存容量的方法
要管理内存,首先得知道操作系统所在的这个计算机内存有多大。检查内存容量的方法很简单,就是从要检查的起始位置到最后位置依次写入一个数值(例如0xaa55aa55),然后按位反转,检查是否正确地完成了反转(变成0x55aa55aa);然后再次反转,检查是否正确地完成了反转。如果反转结果都是正确的,说明这个地址的内存是存在的;否则就说明内存的最大地址就到此为止了。其代码如下。
1 unsigned int memtest_sub(unsigned int start, unsigned int end) 2 { 3 unsigned int i, *p, old, pat0 = 0xaa55aa55, pat1 = 0x55aa55aa; 4 for (i = start; i <= end; i += 0x1000) { 5 p = (unsigned int *) (i + 0xffc); 6 old = *p; /* 先记住修改前的值 */ 7 *p = pat0; /* 试写 */ 8 *p ^= 0xffffffff; /* 反转 */ 9 if (*p != pat1) { /* 检查反转结果 */ 10 not_memory: 11 *p = old; 12 break; 13 } 14 *p ^= 0xffffffff; /* 再次反转 */ 15 if (*p != pat0) { /* 检查是否恢复 */ 16 goto not_memory; 17 } 18 *p = old; /* 恢复为修改前的值 */ 19 } 20 return i; 21 }
但直接用C语言来写这个函数的话,C编译器会把它优化成这个样子。
1 unsigned int memtest_sub(unsigned int start, unsigned int end) 2 { 3 unsigned int i, *p, old, pat0 = 0xaa55aa55, pat1 = 0x55aa55aa; 4 for (i = start; i <= end; i += 0x1000) { 5 //全部被优化掉 了 6 } 7 return i; 8 }
C编译器不会理睬"内存到头了"这种事情,它只在应用程序的层面看问题。所以它认为for循环里的if判定都是必然为真(或为假)的,认为没有被其它代码使用的变量都是没用的。所以它就干脆把这些"没用的"代码删掉了。
为了解决这个问题,还是用汇编语言来写这个memtest_sub函数好了。代码如下。
1 _memtest_sub: ; unsigned int memtest_sub(unsigned int start, unsigned int end) 2 PUSH EDI ; (由于还要使用EBX, ESI, EDI) 3 PUSH ESI 4 PUSH EBX 5 MOV ESI,0xaa55aa55 ; pat0 = 0xaa55aa55; 6 MOV EDI,0x55aa55aa ; pat1 = 0x55aa55aa; 7 MOV EAX,[ESP+12+4] ; i = start; 8 mts_loop: 9 MOV EBX,EAX 10 ADD EBX,0xffc ; p = i + 0xffc; 11 MOV EDX,[EBX] ; old = *p; 12 MOV [EBX],ESI ; *p = pat0; 13 XOR DWORD [EBX],0xffffffff ; *p ^= 0xffffffff; 14 CMP EDI,[EBX] ; if (*p != pat1) goto fin; 15 JNE mts_fin 16 XOR DWORD [EBX],0xffffffff ; *p ^= 0xffffffff; 17 CMP ESI,[EBX] ; if (*p != pat0) goto fin; 18 JNE mts_fin 19 MOV [EBX],EDX ; *p = old; 20 ADD EAX,0x1000 ; i += 0x1000; 21 CMP EAX,[ESP+12+8] ; if (i <= end) goto mts_loop; 22 JBE mts_loop 23 POP EBX 24 POP ESI 25 POP EDI 26 RET 27 mts_fin: 28 MOV [EBX],EDX ; *p = old; 29 POP EBX 30 POP ESI 31 POP EDI 32 RET
知道了内存容量,就可以进行管理了。