《30天自制操作系统》笔记(07)——内存管理

上一篇中处理掉了绝大部分与CPU配置相关的东西。本篇介绍内存管理的思路算法

现在想想,从软件工程师的角度看,CPU也只是一个软件而已:它的功能就是加载指令、执行指令和响应中断,而响应中断也是在加载指令、执行指令。就像火车沿着一条环形铁轨前进;当中断发生时,就好像铁轨岔口处变轨了,火车就顺着另一条轨迹走了;走完之后又绕回来重新开始。决定CPU是否变轨的,就是CPU里的特定寄存器。

《30天自制操作系统》笔记(07)——内存管理

这是题外话,就此为止。

什么是内存管理

假设内存大小是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
汇编版本的memtest_sub

知道了内存容量,就可以进行管理了。

相关文章: