到了十分乏味而又至关重要的部分了,想要一个操作系统运行起来,同时处理不同的程序,就要使用内存管理。
在开始之前,我们给Makefile中所有的gcc命令增加一个-fno-stack-protector,意思是不进行栈溢出检查,这是linux内核程序的一种防止栈溢出漏洞的机制,如果不加这一条指令,在使用栈的时候会发生__stack_chk_fail错误,具体详情百度 linux canary,这次我们做的东西跟linux半点关系都没有,所以就不赘述了。
更正一下内存分布图
把GDT放在0x80000处,这样GDT就一定够用了。GDT最大为64KB
gdt_size: .word 31
gdt_base: .long 0x80000 #定义GDT位置
一、内存管理的原理
为了让操作系统能够统一的管理内存,所以需要让操作系统知道每一个内存地址的使用情况,是可用的,还是不可用的,并采用分页的形式将其管理起来。具体来说就是将内存按一定的大小分为多个页面,这里所说的大小不是唯一的,可根据多方面的条件来权衡,在这里我们将页面大小定义为4KB,也就是4096B(通常情况下也是这样的)。我们假设最大内存为4GB(实际可以更大),采用一个byte来记录4KB的内存是否已经分配出,于是我们需要
0xffffffff÷0x1000=1048576 Bytes = 1024KB = 1MB,我们用0x100000~0x200000来存储这个表格。
一个Byte记录了4KB的内存空间,当第0位置1时,说明内存被使用了,置0说明尚未使用。
我们先把系统的框架完成,以后再来改进这些模块。
二、具体实现
新建memmory文件夹,新建memmory.c和memmory.h,添加进入makefile文件。
InitMMap()函数用于初始化系统内核所占用的内存,这部分内存永远为被使用,将InitMMap()加入main.c
void AllocatePages(int pages,app_memmory_struct *app_memmory)这是内存分配函数,实现原理是遍历内存表,找到连续的pages页内存,然后分配给结构体app_memmory_struct,将内存分配情况输出到屏幕左上角,结构体定义和声明位于下方。
void DeletePages(app_memmory_struct *app_memmory)//删除页面函数
memmory.c
#include "memmory.h"
#include "../font/font.h"
char *mmap = (char *)(0x100000 - 0x8200);//内存位图位置0x100000~0x1fffff,减去段基址
void InitMMap()
{
for (int i = 0; i < MMAP_END - MMAP_START + 1; i++)
{
if (i < MMAP_KERNEL)
{
//前5MB留给内核使用
mmap[i] = MMAP_USED;
}
//剩下的内存为未使用
else
{
mmap[i] = MMAP_FREE;
}
}
}
void AllocatePages(int pages,app_memmory_struct *app_memmory)//申请页面函数
{
static unsigned int y;
unsigned char succ = 0;
unsigned int i,j;
for (i = 0x500; i < MMAP_END - MMAP_START + 1; i++)
{
if (mmap[i] == MMAP_FREE)//空内存
{
for (j=1;j<pages;j++)
{
if(mmap[i+j] != MMAP_FREE)
{
break;
}
if (j == (pages-1))//成功申请到
{
succ = 1;
}
}
if (succ==1)//如果成功申请到,则赋值,修改MMAP,返回
{
app_memmory->start_page = i;
app_memmory->end_page = i+j-1;
for (unsigned int k=i;k<i+j;k++)
{
mmap[k] = MMAP_USED;
}
printf(0,16*y,0xff0000,"i:%x,j:%x",app_memmory->start_page,app_memmory->end_page);
y++;
return;
}
else
{
i = i + j;
}
}
}
app_memmory->start_page = 0;
app_memmory->start_page = 0;
printf(0,16*y,0xff0000,"i:%x,j:%x",app_memmory->start_page,app_memmory->end_page);
y++;
}
void DeletePages(app_memmory_struct *app_memmory)//删除页面函数
{
for (int i = app_memmory->start_page;i<=app_memmory->end_page;i++)
{
mmap[i] = MMAP_FREE;
}
}
memmory.h
void InitMMap();
typedef struct{
unsigned int start_page;
unsigned int end_page;
} app_memmory_struct;
void AllocatePages(int pages,app_memmory_struct *app_memmory);//申请页面函数
void DeletePages(app_memmory_struct *app_memmory);//删除页面函数
#define MMAP_FREE (0)//未使用
#define MMAP_USED (1)//已使用
#define MMAP_START (0x100000)//第一个表的位置
#define MMAP_END (0x1fffff)//最后一个表的位置
#define MMAP_KERNEL (0x500)//5MB以下给kernel使用,初始化置1
在main.c中写几个测试函数,一切正常。
三、改正一个小错误
刚才在写程序的时候竟然栈满了,查了半天终于找到了原因:
system.s最后改为如下
现在我们把0x400000~0x200000作为内核的栈地址,共2MB,妥妥的够用。
start_protect:
.code32
movw $0x0010, %ax
movw %ax, %ds
movw %ax, %ss #暂时公用一个段选择子
movl $0x003f7e00, %esp # 003f7e00 = 0x400000 - 0x8200,之前的值太小了
call SysMain
gdt_size: .word 31
gdt_base: .long 0x80000 #定义GDT位置
现在内存使用情况如下: