MIT6.828 32位操作系统笔记(6)----LAB1下

MIT EDU 6.828 实验源代码

分类 MIT6.828 32位操作系统实验笔记

实验完善代码 LAB2-4下载链接 提取码:79t8

1、JOS内核

回到前面内核可执行文件是如何被boot loader 加载到内存中的,在 boot loader完成了模式转换和将ELF 文件加载到内存之后,就会执行跳转指令。

((void (*)(void)) (ELFHDR->e_entry))();  //boot/main.c

由于内核可执行文件被加载到内存的0x0010000,也就是e_entry 所表示的物理地址。

  • 在内核代码中,如何实现从链接地址到加载地址之间的转换的?
    保护模式下,有 物理地址 = GDT(全局描述符表) 表项中段基址 + 偏移地址,而实际上指针的值通常就是偏移地址,那么段基址是多少呢?

通过命令在进入保护模式之前,可以查看GDT 表的内容,GDT表中一个标识代码段的,另一个标识数据段。可以看到两个段的线性地址都是0,即代表段的基址是0,这个时候也就是说偏移地址就是物理地址。

进入内核之后,GDT表中线性地址的值变为0x10000000,由于在内核执行过程中,偏移地址都是大于0xf0100000, 于是加上0x10000000 后便会造成高位的进位,但是由于地址最多就只有32位,于是实际上高位的进位就被舍去了,这样一来,最后的物理地址便会是内存的低位,在0x100000附近,而不会寻址到0xf0100000这样的不存在的高位物理地址空间。

部分内核代码解析
entry:
	movw	$0x1234,0x472			# warm boot
	movl	$(RELOC(entry_pgdir)), 
	movl	%eax, %cr3
	movl	%cr0, %eax
	orl	$(CR0_PE|CR0_PG|CR0_WP), %eax
	movl	%eax, %cr0
	mov	$relocated, %eax
	jmp	*%eax
relocated:
	movl	$0x0,%ebp			
	movl	$(bootstacktop),%esp 
	call	i386_init
spin:	jmp	spin

.data
###################################################################
# boot stack
###################################################################
	.p2align	PGSHIFT		# force page alignment //强制4K对齐
	.globl		bootstack
bootstack:
	.space		KSTKSIZE  //在inc/memlayout 中定义为32k。
	.globl		bootstacktop   
bootstacktop:  

这是boot loader 将ELF 可执行文件加载到内核,这段可执行文件的第一段代码。这段代码首先加载了新的GDT表,紧接着初始化了堆栈相关的寄存器。

两个全局变量bootstack表示内存中的第一个临时堆栈的位置,尺寸大小为KSTKSIZE; bootstacktop指向这段区域后的第一个字节,刚开始堆栈是空的,因此栈顶就是bootstacktop所指向的位置,于是将bootstacktop的值赋给esp寄存器。

设置完之后程序跳转到i386_init。

void i386_init(void)
{
	extern char edata[], end[];
	memset(edata, 0, end - edata);
	cons_init();
	cprintf("6828 decimal is %o octal!\n", 6828);
	test_backtrace(5);
	while (1)
		monitor(NULL);
}
堆栈解析

堆栈具有先进后出的特点,在实际的内存中,栈低是固定的,栈顶可以变化,向低地址方向生长。
需要出栈一个字的数据,即4个字节,这时候需要当前esp指向位置开始的4个字节,并且在出栈之后将esp +4 。
在这里插入图片描述
需要进栈一个字的数据,首先将esp - 4,并把这个字存在esp指向位置开始的4个字节处。
在这里插入图片描述
分析了解调用函数时,堆栈的变化,我们需要了解这个过程中几个关键的寄存器。

  • eip 存储当前执行指令的下一条指令在内存中的偏移地址;
  • esp 存储指向栈顶的指针
  • ebp存储指向当前函数需要使用的参数的指针

在程序调用函数中,首先会将函数需要的参数进栈,然后将eip 中的一个字进栈(也就是下一条指令在内存中的位置),这样在函数调用结束之后可以通过eip 返回调用函数的程序。一进入调用函数,第一件事就是将ebp 进栈,然后将esp 的值赋给ebp , 这样此时ebp 变指向堆栈中存储ebp,eip和函数参数的地方,所以ebp 通常指向当前函数所需要的参数。所以当一个函数调用另一个函数的时候,被调用的函数执行时的ebp 指向调用她的函数的ebp 值存放的位置。
在这里插入图片描述

2、Exercise 12

在这里插入图片描述
lab/kern/init.c 中调用了test_backtrace(), 其中,mon_backtrace是exercise 12 的内容,这个函数的声明以及定义在lab/kern/monitor.c 文件中

int  mon_backtrace(int argc, char **argv,struct Trapframe *tf)
{
	uint32_t *ebp,*eip;
	uint32_t arg0,arg1,arg2,arg3,arg4;
	ebp = (uint32_t *)read_ebp();
	eip = (uint32_t *)ebp[1];
	arg0 = ebp[2];
	arg1 = ebp[3];
	arg2 = ebp[4];
	arg3 = ebp[5];
	arg4  = ebp[6];
	cprintf("stack backtrace\n");
	cprintf("ebp \t     eip \t   arg0\targ1\targ2\targ3\targ4\t \n");
	while(ebp != 0)	
	{
		cprintf("ebp = %x , eip = %x,  %x     ,     %x     ,     %x     ,     %x     ,     %x  \n",ebp,eip,arg0,arg1,arg2,arg3,arg4);
		ebp = (uint32_t *)ebp[0];//读取调用函数的ebp
		eip = (uint32_t *)ebp[1];
		arg0 = ebp[2];
		arg1 = ebp[3];
		arg2 = ebp[4];
		arg3 = ebp[5];
		arg4 = ebp[6];	
	}	
	return 0;
}

3、Chanllenge exercise (未完待续)

在这里插入图片描述
LAB1上中介绍了显示输出函数的具体内容,根据字符属性修改程序,实现不同颜色的输出。
在这里插入图片描述

追溯一个字符打印的过程,cprintf -> vcprintf -> vprintfmt -> cputchar -> cons_putc -> cga_putc ,关于颜色的控制在函数cga_putc中。

static void cga_putc(int c)
{
	// if no attribute given, then use black on white
	if (!(c & ~0xFF))
		c |= 0x0700;

如果没有赋予颜色属性,默认的属性为白底黑字。

分类 MIT6.828 32位操作系统实验笔记

本文参考文章 http://grid.hust.edu.cn/zyshao/OSEngineering.htm
推荐这位博主系列的文章

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值