前言
我们在阅读Linux源码的时候,总是会有一个疑问,整个系统的入口是在哪个位置,从哪个地方开始阅读,抱着这个疑问,这里记录下我对Linux启动过程的理解。
启动程序
1. main函数
确实,main函数是大多数编程语言的入口函数,操作系统主要是由C语言编写的,因此,main仍然是整个程序的入口。但是,我们也知道在用C语言编写代码的时候,会用到各种函数库,函数内部会有临时变量,甚至会用malloc函数申请内存,这些过程均由编译器自动完成,编写程序的时候也不需要关注这些细节的功能。
其实,一个程序在执行main函数之前,会有一系列的操作,我们可以从main函数的参数直观的看出。
/**
* main函数
* @param argc 程序输入参数个数
* @param argv 程序的输入参数(指针)
* @param env 程序的环境变量(指针)
* @return 执行状态
*/
int main(int argc, char *argv[], char *env[]){
printf("hello word!\n");
return 0;
}
可能有人会有一些疑问,main函数怎么会有三个参数
,其实上面的三个参数均可以忽略,由编译器自动填充参数,因此一般我们看到的main函数的例子有很多的变体。
//有这样的
int main(int argc, char *argv[]){ }
//也有这样的
void main(){ }
既然main函数有参数,说明main本身会作为函数的功能被调用,那么main函数是在哪个位置被调用?其实,main函数会被具有引导启动功能的汇编程序调用(函数调用的过程其实就是操作堆栈的过程,后续再细节探讨)
2. 汇编引导程序
在上面的讨论中,我们提到,C语言编写代码的时候,我们会用到临时变量,用malloc动态申请内存,这些操作均对应着栈空间和堆空间的操作(一个程序在系统中的映射情况如下图,仅仅了解下,后续再讨论下内存管理的细节),所以汇编程序的其中一个功能便是设置堆和栈的地址,当然,也会设置中断处理的入口函数。
为了便于理解,以STM32为例(X86和ARM A系列的操作比较复杂,另外C51系列由于简单,直接嵌入到了IDE中,代码中没有直接体现),打开一个STM32的工程,我们可以看到.s后缀的汇编程序代码,如startup_stm32f407xx.s,打开代码,我们可以看到,其在初始化的时候定义了堆和栈的大小,然后初始化栈指针,并定义中断函数处理的地址,最后通过一个系统调用,reset处理器,然后进入到main函数中。
3. 下一篇
有关Linux相关的启动过程,会在下一篇进行介绍(会切入到X86和ARM A系列处理器的硬件特性)。
参考资料
[1] Unix环境高级编程
[2] Linux内核完全剖析:基于0.12内核