SmlOS八-内存管理系统实现
内存大小的检测
这里使用了是很笨的方法,就是检测指定内存地址是否可写。
写成c大致伪码大概是:
int count_mem_size()
{
int i = 0;
for (int i = 4M ; i < 4G; i+= 0x1000)
{
int *p = i;
*p = 0x11223344;
if (*p != 0x11223344)
{
return i;
}
}
return i;
}
有人可能对这样的代码很好奇,这样写内存,不会内存越界,导致奔溃吗?
第一呢,我们在gdt中设置了可以访问4g的内存段。
第二呢,我们并没有设置相关的idt异常处理函数,所以也不会进入相关的内存操作异常处理函数。
也就是说即使写越界了,也不会收到异常。
所以对于不可访问的内存,所以看到的现象就是,写入内存了一个值,但是读取出来却不一致的情况。
但是这种莫名其妙的操作,很有可能会被C编译器优化掉,所以这部分由汇编实现。
汇编函数中呢,采用了写入再取反再读取的操作,但大致原理是一样的。
内存检测汇编实现:
; 内存检查函数 检查start地址开始到end地址的范围内,能够使用的内存的末尾地址,并将其作为返回值返回
_memtest_sub: ; unsigned int memtest_sub(unsigned int start, unsigned int end)
PUSH EDI ; 保存EDI ESI EBX
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
关闭高速缓存
高速缓存简介:
大致就是将内存中经常访问的地址内容存在高速缓存中,在计算机读取内存内容中的时候,先从高速缓存读取,以提升读取速度。
我们需要检测之前关闭高速缓存,以免检测不准确。
//用于设置和清除CR0的NW与CD位,设置高速缓存用
#define CR0_CACHE_DISABLE 0x60000000
//禁止高速缓存
cr0 = load_cr0();
cr0 |= CR0_CACHE_DISABLE;
store_cr0(cr0);
内存分配管理
我们需要构造一个模块,统一分配和管理内存。
就是malloc函数中,系统底层是如何决定分配哪些内存给你呢。
系统需要做的是:
- 1.确保分配给你的内存是没有占用的
- 2.有足够高的效率和内存记录结构体的占用
我们使用的方式是:只记录未使用内存。
比如说系统有100M内存,刚开始只是内核相关占据了1M:
那么内存管理表记录就是:
空闲地址 | 大小 |
---|---|
1M开始 | 99M |
然后我分配了10M内存:(分配空间 1M-11M)
空闲地址 | 大小 |
---|---|
11M开始 | 89M |
然后由分配了5M:(分配空间 11M-16M)
空闲地址 | 大小 |
---|---|
16M开始 | 84M |
然后释放了上面申请的10M:
空闲地址 | 大小 |
---|---|
1M开始 | 10M |
16M开始 | 84M |
这里的空闲内存条目变成了两条。
下次分配会找第一个满足条件的内存块。
这样的好处是,可以用很少的内存管理相关内存,记录我们需要的信息。
然后我们需要维护一个内存管理表。
内存管理表的结构如下:
struct MEMMAN
{
int frees, maxfrees, lostsize, losts;
struct FREEINFO free[MEMMAN_FREES];
};
内存管理结构
- frees:内存可用信息条目的数目
- maxfrees: frees的最大值
- lostsize: 释放失败的内存大小的总和
- losts: 释放失败的次数
- struct FREEINFO free[MEMMAN_FREES];
内存可用信息条目数组
单块内存信息结构体:
内存可用信息条目
struct FREEINFO
{
unsigned int addr, size;
};
- addr: 可用内存块首地址
- size: 可用内存块大小
内存分配
参考上面的例子说明。
内存分配算法简要来说就是,在内存分配函数中,首先需要在管理表中遍历,找到第一个可用内存,然后再 内存中割除这一部分。
向上取整
主要目的是为了减少内存空洞的可能性。
当然如果申请的内存过于零碎,重复的释放和申请,可能会超过内存管理表空闲块最大记录数,所以分配的时候一般会以4K向上取整。
4K向上取整的意思就是,无论需要分配内存的长度,最低都会分配4K,如果超过4k,也会分配4K的倍数。
比如你申请1byte,会分配给你4k大小。
你申请4k + 1byte,会分配给你8k大小。
核心计算语句:
//向上以4K取整
size = (size + 0xfff) & 0xfffff000;
内存释放
内存的释放主要对内存管理表进行操作,其中需要注意是,
释放的内存是否可以和表中空闲段段合并成一段:
-
1.可以合并的话,尽量合并
-
2.不能合并,分拆出一个空闲记录条目,加入内存管理表
发表回复
要发表评论,您必须先登录。