day7和day8都是鼠标和32位的操作,看起来都是理论,先略过。
检测内存块数量
- 使用中断去检测内存,把这个当成一个函数调用吧。这里面di寄存器给的地址就是结果存在的内存位置。
ComputeMemory:
mov ebx, 0
mov di, MemChkBuf
.loop:
mov eax, 0E820h
mov ecx, 20
mov edx, 0534D4150h
int 15h
jc LABEL_MEM_CHK_FAIL
add di, 20
inc dword [dwMCRNumber]
cmp ebx, 0
jne .loop
jmp LABEL_MEM_CHK_OK
LABEL_MEM_CHK_FAIL:
mov dword [dwMCRNumber], 0
- C语言当中,可以通过下面的函数去获取内存的块数。这个函数也给我们其实,C语言中的return x,中的x存储在eax中。这里也可以看出,x86cpu把内存分成了多个块。没个块对应不同的内存区间。
get_memory_block_count:
mov eax, [dwMCRNumber]
ret
- c语言中可以直接现实,但是需要把int值转换一下。
char* intToHexStr(unsigned int d) {
static char str[11];
str[0] = '0';
str[1] = 'X';
str[10] = 0;
int i = 2;
for(; i < 10; i++) {
str[i] = '0';
}
int p = 9;
while (p > 1 && d > 0) {
int e = d % 16;
d /= 16;
if (e >= 10) {
str[p] = 'A' + e - 10;
} else {
str[p] = '0' + e;
}
p --; // 这里注意 原来的老师没有加
}
return str;
}
- 调用就用以下代码调用
int memCnt = get_memory_block_count();
char* pStr = intToHexStr(memCnt);
showString(vram, xsize, 0, 0, COL8_FFFFFF, pStr);
}
- 我这边显示有6个内存块。在makefile中修改qemu的内存参数,
qemu-system-i386 -m 32M -drive file=myos.img,if=floppy
把内存调大到4096M,内存块会增加到7,不知道为啥。
内存检测具体信息
这里用c语言写的init_pic有点小问题,还是换回init8259A的汇编调用吧。
- 添加对int 15中断的内存地址获取。
get_adr_buffer:
mov eax, MemChkBuf
ret
- c语言中调用就使用一下函数
struct AddrRangeDesc* memDesc = (struct AddrRangeDesc*)get_adr_buffer();
- 键盘时间判断一下回车对应的key值 0x1c
if (data == 0x1C) {
showMemoryInfo( memDesc + count, vram, count, xsize, COL8_FFFFFF);
count = (count+1);
if (count > memCnt) {
count = 0;
}
}
- 运行一下发现,按一次回车,会读取一个内存块的内存信息。这里我总结了一下内存的排布信息。其中第四段最大的那个内存块,类型为1,表示可以占用。1EE0000换算成十进制是30M的内存。所以,下一步,我们就在这块内存当中,实现我们的内存分配算法
base:0~9fc00 length:9fc00 type:1
base:9fc00~A0000 length:400 type:2
base:f0000~100000 length:10000 type:2
base:100000~1fe0000 length:1EE0000 type:1
base:1fe0000~2000000 length:20000 type:2
base:FFFC0000~100000000 length:40000 type:2
内存分配算法
- 我觉得这块直接抄就行了,我们把它当成C单中的malloc和free算了,没必要知道其中的原理。代码在这里
- 这里吧mem_util.c的函数放在了另外一个文件里面,那么就牵扯到一个.o合并的问题。
i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o mem_util.o mem_util.c
i386-elf-ld -m elf_i386 -r write_vga.o mem_util.o -o ckernel.o
objconv -fnasm ckernel.o write_vga.asm
- main函数中调用一下total函数看一下对不对,这里面跟MyOperatingSystem的作者给出的起始地址不太一样,按他的解释应该是 0x100000后面加上32k的结构体。也就是0x108000开始,但是整个长度就要相应的减去32k,其实应该是1EE0000-0x8000=0x1fd8000,我这里给的内存还是32M,MyOperatingSystem的作者用的是1024m。但是应该无所谓的,因为我们这个系统看起来也用不了那么多的内存。
memman_init(memman);
memman_free(memman, 0x00108000, 0x1fd8000);
int memtotal = memman_total(memman) / (1024*1024);
char* pMemTotal = intToHexStr(memtotal);
showString(vram, xsize, 0, 0,COL8_FFFFFF, "total mem is: ");
showString(vram, xsize, 17*8, 0, COL8_FFFFFF, pMemTotal);
showString(vram, xsize, 28*8, 0, COL8_FFFFFF, " MB");
- 跑起来,给出的结果是31M内存,差不多,可以继续往前了。