一、简介
内核启动介绍了整个系统从硬件上电如何一步步进入用户程序的过程。一般情况下,启动过程分为硬件上电,首先运行和体系架构相关的启动汇编文件,进行一些最基本硬件的初始化 (例如 CPU 配置,时钟,栈地址,RAM 等),为内核运行铺垫好环境,然后初始化内核各模块 (例如调度器,定时器等),接着创建系统任务 (例如空闲任务) 和用户任务,最后启动调度和运行用户程序。
二、自动初始化
自动初始化是指初始化函数不需要被显式调用。
隐式调用(静态调用):将动态链接库和其它源程序文件(或者目标文件)一起参与链接;在介绍内核启动之前,有必要介绍自动初始化的实现机制。
在介绍内核启动之前,有必要介绍自动初始化的实现机制。
显式调用(动态调用):手动调用动态链接库中包含的资源,同时用完后要手动将资源释放。自动初始化的实现是利用编译器的段和段排序的特性,以 GCC 编译器为例具体如下:
在介绍内核启动之前,有必要介绍自动初始化的实现机制。
自动初始化的实现是利用编译器的段和段排序的特性,以 GCC 编译器为例具体如下:
1、通过 GCC 的关键字attribute((section (x))),可以把定义的符号(函数地址或变量地址)放到段名为 x 的段中,段名 x 由用户指定。在自动初始化应用中,段名都包含.init_call 字符串,若采用通配符的表示方法,段名为.init_call,表示任何一串其它一串字符串;
2、在 GCC 的链接脚本中,关键字 KEEP ((SORT (.init_call))) 可以对段名符合.init_call * 形式的段进行排序;
3、若把自动初始化函数的符号(即函数地址)放到符合.init_call * 形式的段中,就可以间接实现函数符号排序,从而可以提供有顺序的执行自动初始化函数的方法。
自动初始化通过宏定义提供了注册初始化函数的接口,接口如下:
提供了段结构的宏定义,根据初始化优先级的不同,有如下宏定义:
编译之后,初始化宏对应的段结构如下:
三、内核启动流程
下图展示了整个系统启动过程和内核启动过程,本节主要介绍内核启动部分。
可见,内核启动主要有下面几个
步骤:
1、系统先从启动文件开始运行,然后进入 OneOS 的内核启动函数 os_kernel_init () 和 os_kernel_start ()
2、os_kernel_init 中调用_k_run_init_call 函数执行 OS_INIT_LEVEL_PRE_KERNEL_1,进行内核启动前的第一阶段的初始化
3、初始化内核各模块,如 tick 队列,调度器,定时器等
4、创建 recycle,idle,timer,sys 系统任务
5、启动调度器,最后会运行 sys 任务,sys 任务调用_k_run_init_call 函数依次执行其他自动化宏注册的函数,最后调用 main 函数进入用户程序入口