Linux内核源代码分析——Linux内核的入口
Jack:hi,淫龙,在Linux内核的源代码里,有几段汇编代码,那几段代码是负责Linux内核引导的。
我:是的。早期的Linux内核引导代码只有bootsect.s、setup.s、head.s这3个文件,这三个文件都是Linus在1991年左右亲手写的。后来的代码虽然进行了加固,但是原型还是这几个。
Jack:我想弄清楚。这几个汇编代码做了些什么事情。
我:我不能在对话中一句代码一句代码地给你进行注释,这是不现实的。我希望能把你最困惑的知识点解释清楚,起到画龙点睛的作用,而不是手把手地教你它是什么。
Jack:那我提一个最困惑的问题吧。Linux的内核是如何开始执行main()函数的?(在1.0之后?的代码中,所有的Linux入口函数已经改名为start_kernel())。
我:我给你看一段head.s的代码吧。
head.s line 135-141
after_page_tables: pushl $0 # These are the parameters to main :-) pushl $0 pushl $0 pushl $L6 # return address for main, if it decides to. pushl $_main # 把main函数的地址压入栈内,等待出栈,指令跳转 jmp setup_paging # 跳转到setup_paging标签
head.s line 210-218
1: stosl /* fill pages backwards - more efficient :-) */ subl $0x1000,%eax jge 1b xorl %eax,%eax /* pg_dir is at 0x0000 */ movl %eax,%cr3 /* cr3 - page directory start */ movl %cr0,%eax orl $0x80000000,%eax movl %eax,%cr0 /* set paging (PG) bit */ ret /* this also flushes prefetch-queue */#弹栈,调用_main
关键就在这里:入栈函数地址,ret返回顺便调用_main。
Jack:明白了。原来是这样。我再问一个问题。Linux的内核的入口函数为什么可以是start_kernel()——C语言的入口地址不是main()吗?
我:这个问题你可以尝试从汇编的角度来思考。C语言的入口地址不是main()吗?——这个是不一定的。仅仅从C语言的角度来思考,这话是对的。如果从汇编的角度来思考,这话就不对了。
Jack:从汇编的角度来思考C语言?我操。
我:你用gcc -S选项编译一个source file,会生成一个汇编源代码文件。之后才会生成二进制文件。C只是一层皮,真正的骨在于汇编。
Jack:你的意思是,C其实是需要翻译成汇编的,从汇编的层面来思考代码才能把握本质的东西。
我:great。你明白了。