自制操作系统 内存管理
Posted NONE
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自制操作系统 内存管理相关的知识,希望对你有一定的参考价值。
2016.07.04 2016.07.05 2016.07.06
参考书籍:《30天自制操作系统》、《自己动手写操作系统》
所以你可以看到,计算机的目的就是要运行程序。而操作系统是一个在计算机上运行的程序,目的是帮助计算机在人类的操作下更好的运行程序。
这一章的内存管理便是操作系统作为boss程序的一种体现。操作系统管理内存是因为,计算机要运行程序需要内存,所以操作系统作为程序的大boss,来负责管理并给应用程序(小弟们)分配、回收内存。
首先,内存管理要涉及缓存的概念。在我们做内存检查时要将缓存设为OFF。
高速缓存位于主存与CPU之间,作为一种缓冲,因为CPU太快,主存存取速度太慢。高速缓存则是一种存取速度接近CPU的存储器。其工作方式就在于:访问内存前,先将要访问的地址和内容存入高速缓存里。往内存里写入数据时也一样。首先更新高速缓存的信息,再写入内存。
更细致地讲,高速缓存的内存空间与内存是存在一定映射规则的:1.全相连映射 2.直接相连映射 3.组相连映射 具体不多谈。因为本篇并不涉及那么深的内容。
本章需要的是内存管理,最基本的是内存检查。要检查内存,需要往内存里随便写入一个值,然后读取,来检查读取的值与写入的值是否相等。若相等(说明这里是可用的内存),这时需要先关闭缓存,否则会写在缓存里,而不是内存。
下面C函数的目标是关闭高速缓存:
unsigned int memtest(unsigned int start, unsigned int end) { char flg486 = 0; unsigned int eflg, cr0, i; /* 确认CPU是386还是486以上的 */ eflg = io_load_eflags(); eflg |= EFLAGS_AC_BIT; /* AC-bit = 1 */ io_store_eflags(eflg); eflg = io_load_eflags(); if ((eflg & EFLAGS_AC_BIT) != 0) { /* 如果是386、即使设定AC=1,AC的值还是会回到0 */ flg486 = 1; } eflg &= ~EFLAGS_AC_BIT; /* AC-bit = 0 */ io_store_eflags(eflg); if (flg486 != 0) { cr0 = load_cr0(); cr0 |= CR0_CACHE_DISABLE; /* 禁止缓存 */ store_cr0(cr0); } i = memtest_sub(start, end); if (flg486 != 0) { cr0 = load_cr0(); cr0 &= ~CR0_CACHE_DISABLE; /* 允许缓存 */ store_cr0(cr0); } return i; }
上面C语言需要调用下面两个汇编函数,因为为了禁止缓存,需要对CR0寄存器的某一标志位进行操作:
_load_cr0: ; int load_cr0(void); MOV EAX,CR0 RET _store_cr0: ; void store_cr0(int cr0); MOV EAX,[ESP+4] MOV CR0,EAX RET
至于内存检查部分,用的是汇编语言函数:
_memtest_sub: ; unsigned int memtest_sub(unsigned int start, unsigned int end) PUSH EDI PUSH ESI PUSH EBX MOV ESI,0xaa55aa55 ; pat0 = 0xaa55aa55; MOV EDI,0x55aa55aa ; pat1 = 0x55aa55aa; MOV EAX,[ESP+12+4] ; i = start; mts_loop: MOV EBX,EAX ADD EBX,0xffc ; p = i + 0xffc; MOV EDX,[EBX] ; old = *p; MOV [EBX],ESI ; *p = pat0; XOR DWORD [EBX],0xffffffff ; *p ^= 0xffffffff; CMP EDI,[EBX] ; if (*p != pat1) goto fin; JNE mts_fin XOR DWORD [EBX],0xffffffff ; *p ^= 0xffffffff; CMP ESI,[EBX] ; if (*p != pat0) goto fin; JNE mts_fin MOV [EBX],EDX ; *p = old; ADD EAX,0x1000 ; i += 0x1000; CMP EAX,[ESP+12+8] ; if (i <= end) goto mts_loop; JBE mts_loop POP EBX POP ESI POP EDI RET mts_fin: MOV [EBX],EDX ; *p = old; POP EBX POP ESI POP EDI RET
上述代码很好懂,from start to end循环向内存写入0xaa55aa55(在那之前先将内存原本值存入edx寄存器),再使得该内存地址与0xffffffff做XOR异或操作。若此时该内存里值为0x55aa55aa,则该内存有效。将edx寄存器里的值放回去就好。
以上便是内存检查的部分的函数,在内存检查的基础上,内存管理要做的两件事一是内存分配,一是内存释放。例如现在要启动一个应用程序,需要nKB内存,其需要的内存便由操作系统来分配。应用程序结束后再收回。
关于内存管理,很多前辈大神设计过很多牛b的算法~当然那是要应用于大型商业操作系统的情况,我们的sonnos只是个小demo,所以只需要一个简单的方法就好。
那就是列表管理法:
把类似“从xxx号地址开始的yyy字节的空间是空着的”这种信息列在表里。
struct FREEINFO { /* 可用内存信息 */ unsigned int addr, size; }; struct MEMMAN { /* 内存管理 */ int frees, maxfrees, lostsize, losts; struct FREEINFO free[MEMMAN_FREES]; };
如果用java描述,大概是这样的结构:
class { private List<FREEINFO> freeInfos; private int frees; private int maxfrees; private int lostsize; private int losts; }
可以看出FREEINFO是这个内存List的单元,该单元,用addr和size标志地址和大小。
基于这样的数据结构,写出的内存分配程序:
void memman_init(struct MEMMAN *man) { man->frees = 0; /* 可用信息数目*/ man->maxfrees = 0; /* 用于观察可用状况:frees的最大值 */ man->lostsize = 0; /* 释放失败的内存大小总和 */ man->losts = 0; /* 释放失败次数 */ return; } unsigned int memman_total(struct MEMMAN *man) /* 报告空余内存大小的合计 */ { unsigned int i, t = 0; for (i = 0; i < man->frees; i++) { t += man->free[i].size; } return t; } unsigned int memman_alloc(struct MEMMAN *man, unsigned int size) /* 分配 */ { unsigned int i, a; for (i = 0; i < man->frees; i++) { if (man->free[i].size >= size) { /* 找到了足够大的内存 */ a = man->free[i].addr; man->free[i].addr += size; man->free[i].size -= size; if (man->free[i].size == 0) { /* 如果free[i]变成了0,就减掉一条可用信息 */ man->frees--; for (; i < man->frees; i++) { man->free[i] = man->free[i + 1]; /* 代入结构体 */ } } return a; } } return 0; /* 没有可用空间 */ }
释放内存函数:
int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size) /* 释放 */ { int i, j; /* 为便于归纳内存,将free[]按照addr的顺序排列*/ /* 所以,先决定应该放在哪里 */ for (i = 0; i < man->frees; i++) { if (man->free[i].addr > addr) { break; } } /* free[i - 1].addr < addr < free[i].addr */ if (i > 0) { /* 前面有可用内存 */ if (man->free[i - 1].addr + man->free[i - 1].size == addr) { /* 可以与前面的可用内存归纳到一起 */ man->free[i - 1].size += size; if (i < man->frees) { /* 后面也有 */ if (addr + size == man->free[i].addr) { /* 也可以与后面的可用内存归纳到一起 */ man->free[i - 1].size += man->free[i].size; /* man->free[i]删除 */ /* free[i]变成0后归纳到前面去 */ man->frees--; for (; i < man->frees; i++) { man->free[i] = man->free[i + 1]; /* 结构体赋值 */ } } } return 0; /* 成功完成 */ } } /* 不能与前面的可用空间归纳到一起*/ if (i < man->frees) { /* 后面还有 */ if (addr + size == man->free[i].addr) { /* 可以与后面的内容归纳到一起 */ man->free[i].addr = addr; man->free[i].size += size; return 0; /* 成功完成 */ } } /* 既不能与前面的归纳到一起,也不能与后面的归纳到一起 */ if (man->frees < MEMMAN_FREES) { /* free[i]之后的,向前移动,腾出一点可用空间 */ for (j = man->frees; j > i; j--) { man->free[j] = man->free[j - 1]; } man->frees++; if (man->maxfrees < man->frees) { man->maxfrees = man->frees; /* 更新最大值 */ } man->free[i].addr = addr; man->free[i].size = size; return 0; /* 成功完成*/ } /* 不能往后移动 */ man->losts++; man->lostsize += size; return -1; /* 失败 */ }
以上是关于自制操作系统 内存管理的主要内容,如果未能解决你的问题,请参考以下文章