linux内核设计的艺术-开始执行main函数

为了执行linux内的C语言main函数,上一篇讲到了,为了从汇编语言环境跳转到C语言环境下执行,将CPU工作模式从16位转变到32位模式(C语言是32位的),并且重新建立了GDT与IDT,但是此时GDT和IDT中并没有内容,所以不能进行内存寻址与中断,接下来就是初始化GDT和IDT了。

进入32位模式后,寄存器也将变为32位寄存器,下面的汇编语法和之前的intel汇编有些不同,为AT&T汇编,至于差别不在赘述。


Head.S
startup_32:
	//重设段寄存器内容
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	mov %ax,%fs
	mov %ax,%gs
    //初始化栈指针,即将栈顶指针指向栈底
	lss _stack_start,%esp
    //建立IDT
	call setup_idt
//建立GDT
	call setup_gdt
    //建立好GDT和IDT后重新设置段寄存器
	movl $0x10,%eax		
	mov %ax,%ds		
	mov %ax,%es		
	mov %ax,%fs
	mov %ax,%gs
	lss _stack_start,%esp
	xorl %eax,%eax
    //检查A20是否成功打开
1:	incl %eax		
	movl %eax,0x000000	
	cmpl %eax,0x100000
	je 1b

    //检查8087数学协处理器后,开始进行内存内页
	call check_x87
	jmp after_page_tables

after_page_tables:
    //以下为将main函数的参数入栈
	pushl $0		
	pushl $0
	pushl $0
	pushl $L6
	//将main函数的地址入栈
	pushl $_main
    //开始进行内存分页
	jmp setup_paging


上面的push _main操作很重要,因为在之后的ret指令后将会将栈中的_main函数地址装入cs,和ip中,参数也会装载到相应的寄存器中,这些操作都将在setup_paging后完成


setup_paging:
	//初始化前5k的内存
	movl $1024*5,%ecx		
	xorl %eax,%eax
	xorl %edi,%edi			
	cld;
rep;
stosl
//将0x0000处赋值为0x1000 + 7,7的意思是111代表访问权限,0x1000作为页表基地址,接下来就分别映射0x2000,0x3000,0x4000
	movl $pg0+7,_pg_dir
	movl $pg1+7,_pg_dir+4		
	movl $pg2+7,_pg_dir+8		
	movl $pg3+7,_pg_dir+12		
	movl $pg3+4092,%edi
	movl $0xfff007,%eax	
    //上述代码其实是建立4096字节的内核页表,每一个字节可以寻址到一个4k的页面,所以可以寻址的空间为16M	
	std
1:	stosl			
	subl $0x1000,%eax
	jge 1b
	xorl %eax,%eax		
	movl %eax,%cr3		
	movl %cr0,%eax
	orl $0x80000000,%eax
	movl %eax,%cr0	
    //调用ret开始执行main函数	
	ret	   


下面是一张内存分页的图,寻址是需要先根据pg_dir的地址找到页表目录,在页目录中找到pg页表,然后在页表中找到相应的页面



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。