内存容量检查
bootpack.c节选
//内存检查
i = memtest(0x00400000, 0xbfffffff) / (1024 * 1024);
sprintf(s, "memory %dMB", i);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 32, COL8_FFFFFF, s);
///////////////////////////////////////////////////////
#define EFLAGS_AC_BIT 0x00040000
#define CR0_CACHE_DISABLE 0x60000000
unsigned int memtest(unsigned int start, unsigned int end)
{
char flg486 = 0;
unsigned int eflg, cr0, i;
/* 确认CPU是386还是486以上的,如果是486以上,EFLAGS寄存器的第18位应该是AC标志位 */
eflg = io_load_eflags();
eflg |= EFLAGS_AC_BIT; /* 将AC置为1 */
io_store_eflags(eflg);
eflg = io_load_eflags(); //然后再读出EFLAGS的值
if ((eflg & EFLAGS_AC_BIT) != 0) /* 如果是386,即使设定AC=1,AC的值还是会自动回到0 */
{
flg486 = 1;
}
eflg &= ~EFLAGS_AC_BIT; /* 将AC置为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;
}
nasfunc.nas节选
GLOBAL _load_cr0, _store_cr0
GLOBAL _memtest_sub
_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 ; 由于还要使用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
内存管理
bootpack.c节选
unsigned int memtotal;
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
memtotal = memtest(0x00400000, 0xbfffffff);
memman_init(memman);
memman_free(memman, 0x00001000, 0x0009e000); /* 0x00001000 - 0x0009efff */
memman_free(memman, 0x00400000, memtotal - 0x00400000);
sprintf(s, "memory %dMB free : %dKB",memtotal / (1024 * 1024), memman_total(memman) / 1024);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 32,COL8_FFFFFF, s);
///
#define MEMMAN_FREES 4090 /* 大约是32KB */
#define MEMMAN_ADDR 0x003c0000
struct FREEINFO /* 可用信息 */
{
unsigned int addr, size; //内存起始地址,大小
};
struct MEMMAN /* 内存管理 */
{
int frees, maxfrees; //可用内存数,最大可用内存数
int lostsize, losts; //释放失败的内存总容量,释放失败的次数
struct FREEINFO free[MEMMAN_FREES];
};
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]删除 */
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; /* 失败 */
}
改善内存管理
之前是以字节为单位进行管理,容易出现很多不连续的小段未使用空间,现在以0x1000字节为单位进行内存分配和释放
memery.c节选
unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size)
{
unsigned int a;
size = (size + 0xfff) & 0xfffff000; //按0x1000向上取整
a = memman_alloc(man, size);
return a;
}
int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size)
{
int i;
size = (size + 0xfff) & 0xfffff000;
i = memman_free(man, addr, size);
return i;
}
总结一下内存容量检查:
1、确认CPU是386还是486以上,如果是486以上的话,禁止缓存,386的话不用,因为386没有缓存。
2、进行内存检查。先保存内存中的值,然后往内存中写入一个值,接着反转,检查反转后的结果是否正确,然后再反转,检查是否恢复,最后恢复内存之前的值。如果在某个环节出错,则终止,返回出错处的地址。
3、允许缓存。
总结一下内容管理:
1、设置4096条内存管理信息,每一条管理信息中包括内存起始地址,内存大小。
2、对内存管理进行初始化,主要是将frees(可用管理信息数)设为0。
3、memman_total计算可用内存的合计大小,并返回。
4、memman_alloc进行内存分配。从编号为0管理信息表中开始找,找到足够大的内存,然后进行分配,修改该条管理信息,如果该条管理信息所管理的区域没有剩余的内存,则可用信息数frees减1,并将后面的管理信息位置向前移动,覆盖掉当前管理信息的位置。
5、memman_free进行内存释放。首先所释放内存的合适位置,然后查看它是否可与前面的可用内存合并,如果能则合并,再查看它是否能与后面的可用内存合并,如果能则合并。如果都不能合并,则后面的内存后移,空出一个空间存放该释放后的内存的管理信息,并且可用用信息数加1,如果需要更新最大可用信息数。如果后移失败,即内存管理信息数超出了设定的最大值,报告释放失败。