这篇分析有点长了,慢慢看~~。
请随手携带一本《32位微机原理》宝典,以备查询。
这是kernel目录下的Makefile,很简单
CFLAGS = -I../include/ -c -Wall -fno-stack-protector -o
all: kernel.o page.o idt.o printk.o keyboard.o irq.o i8259.o console.o
clean:
rm -f *.o *~
kernel.o:
gcc ${CFLAGS} kernel.o kernel.c
page.o:
gcc ${CFLAGS} page.o page.c
idt.o:
gcc ${CFLAGS} idt.o idt.c
printk.o:
gcc ${CFLAGS} printk.o printk.c
keyboard.o:
gcc ${CFLAGS} keyboard.o keyboard.c
irq.o:
gcc ${CFLAGS} irq.o irq.c
i8259.o:
nasm -felf i8259.s
console.o:
gcc ${CFLAGS} console.o console.c
我们先来看目录下的kernel.c文件,主函数就在这里,也就是sagalinux的“内核"核心。。。。我们从主函数发散出去逐个研究。
#include "kernel/idt.h"
#include "kernel/page.h"
#include "kernel/kernel.h"
#include "kernel/keyboard.h"
#include "kernel/console.h"
int main(void)
{
// int i;
char* message = "\nWelcome!\n";
char* kernel_release = "Kernel on an i386\n\n";
char* fake_login = "[LinuxSaga]# ";
page_init();//初始化页机制,开启分页,映射了0~16M的物理页
trap_init();//初始化陷阱门
keyboard_init();//初始化键盘
con_clear();//清除终端的所有字符
printk("%s",message);
printk("%s",kernel_release);
printk("%s",fake_login);
// i = 3 / 0;
while(1);
return 0;
}
简单吧,下面是每一个初始化函数的详细
/kernel/page.c 用于初始化页表,页表中描述一个4K的物理页的一个描述项叫做PTE,PTE大小为4B,
如果PTE在一个4K的物理页中连续存放,形成一个表,我们用PDE来描述这个PTE表,一个PDE大小为4B。
就是这一回事。
#include "kernel/page.h"//一些宏定义
#include "asm/system.h"
//初始化页表
void page_init()
{
int pages = NR_PGT;//PTE表的描述项PDE的个数
// NR_PGT 为4,创建4个PTE表的描述项(PDE),每个PDE占4B,描述了一个PTE表,每个PTE描述了一个4K的页,这样总共可以访问4*1024*4K即16M大小的内存
unsigned int page_offset = PAGE_OFFSET;//#define PAGE_OFFSET (unsigned int)0x2000 (8K)
//PDE项的开始物理地址,PGD_BASE=0x1000(4K),也就是说PDE位于物理内存的第二个页框内(每个page为4K)
unsigned int* pgd = PGD_BASE;//#define PGD_BASE (unsigned int*)0x1000 PGD_BASE 是一个指针,指向unsigned int类型,指针的值是0x1000
//赋值之后pgd指向了0x1000这个内存地址,注意pgd这个指针变量占用4B,
unsigned int phy_add = 0x0000;// 从物理地址的最低端开始建立PTE项的映射,映射内存地址0~16M
// PTE表从物理内存8K处开始,每个PTE表有1024项,占满一个物理页。共有4个PTE表,且连续存放
unsigned int* pgt_entry = (unsigned int*)0x2000; //PTE表的开始地址(8K)
// 填充页目录表PDE项
// 这里依次创建4(pages=4)个页目录表PDE,一个PDE就是一个PTE表的描述项
//(每个PTE表有1024项,1024*4B=4K,占一个物理页)
while (pages--)
{
*pgd++ = page_offset |PTE_USR|PTE_RW|PTE_PRE;
page_offset += 0x1000;//继续描述下一个PTE表
}
//PDE表,有四项PDE:0x1000~0x2000
//PTE表:共四个表,位于0x2000~0x3000, 0x3000~0x4000, 0x4000~0x5000, 0x5000~0x6000
pgd = PGD_BASE;//恢复pgd为PDE表基地址0x1000,4K
// 在页表中填写页到物理地址的映射关系,映射了16M大小的物理内存 即0~16M的物理内存
//共映射了0x1000(4K)个PTE,每个PTE指向一个4K的物理页,共4K*4K=16M内存
//这里因为PTE表是连续的,所以可以直接给所有的PTE赋值,(*pgt_entry++)
while (phy_add < 0x1000000) {//0x1000000=16M
*pgt_entry++ = phy_add |PTE_USR|PTE_RW|PTE_PRE;//下一个PTE项,每个PTE项描述了一个物理页
phy_add += 0x1000;//下一个物理页,共有4K个PTE,每个PTE一次赋值,共循环了4K次
}
//嵌入式汇编语法
//__asm__(assembler template
// : output operands /* optional *//
// : input operands /* optional */
// : list of clobbered registers /* optional */
//);
// 启用页机制, cr3指向页目录根PDE表的开始
__asm__ __volatile__ ("movl %0, %%cr3;"//pgd-->cr3,PDT基地址
"movl %%cr0, %%eax;"
"orl $0x80000000, %%eax;"
"movl %%eax, %%cr0;" //eax-->cr0,cr0最高位置1,用于开启分页
:
:"r"(pgd)//%0代表pgd
:"memory","%eax");
//Invalidate TLB before and after setting up page tables
//使TLB无效,在多核处理器上,避免其他cpu引用到无效的页表
invalidate_tlb();
}
回到主函数中下一个trap_init()函数,位于./kernel/idt.c中
#include "kernel/idt.h"
#include "kernel/kernel.h"
#include