Android系统 lk启动流程简析

本文简要介绍Android系统lk的启动流程,包括LK入口、kmain函数、bootstrap2线程分析,以及apps_init和aboot_init函数的作用。lk启动涉及设置中断向量表、线程系统初始化、硬件外设初始化,最终启动Linux内核。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本篇文章是对初步学习Android系统lk启动流程的一个大致简介。方便掌握lk启动流程的大致框架,具体细节后续再进行更新

1. 前言

需要了解的文件类型:
1)编译LK的链接文件(.ld)
2)汇编文件(.s、.S文件)
3).mk文件(用来向编译系统描述源代码,并将源文件分组为模块。可用于预定义变量)
4)makefile文件(包含一些规则告诉make编译哪些文件以及怎样编译这些文件)
5)Kconfig文件(编译前的文件,其中主要作用是在内核配置的时候,作为配置选项)
6).config文件(文件是在进行内核配置的时候,经过配置后生成的内核编译参考文件)

文件之间的顺序关系:
编写Kconfig—>进行make menuconfig---->生成.config----->编写Makefile---->按照Makefile编译规则进行编译---->编译成功

*拓展思考:

1、lk加载完成后,linux kernel又是怎样加载的?里面又做了些什么操作?
2、关于boot、kernel、HAL等不同层级,程序是怎样被加载、不同层级如何分化完成上层应用的请求任务?

2. LK入口

确定LK的入口,必须要先知道编译LK的链接文件
相关的链接文件为:
bootable/bootloader/lk/arch/arm/system-onesegment.ld

从链接文件中,可以确定LK启动入口为_start函数,该函数的定义在汇编文件:
_start函数的定义在汇编文件:
bootable/bootloader/lk/arch/arm/ctr0.S
_start函数的主要功能是设置中断向量表、初始化bss段、初始化与处理器架构的相关寄存器、搭建C运行环境等;

3. kmain函数

当_start函数设置完成后,然后开始运行bl kmain代码,跳转到kmain函数处运行,进入的C语言的世界
kmain入口(ctr0.S文件,line.187行)

bl kmain    /*跳到kmain函数执行*/b .

在_start函数的最后,将会调用kmain函数,该函数的定义在文件:
bootable/bootloader/lk/kernel/main.c

对于kmain函数实现的主要功能:
1)函数调用后,首先是对早期的thread线程系统进行初始化,
2)接下来则是调用arch_early_init()函数,对CPU处理器架构相关的早期初始化,例如关闭cache,使能mmu等功能,
3)然后开始调用与平台早期初始化的相关函数,对早期需要使用的外设进行初始化,例如中断控制器、debug串口等外设,
4)接下来,则是调用函数搭建出一个完整的thread线程系统,并对lk中的定时器进行初始化,
5)调用thread_create()函数创建出"bootstrap2"线程,并调用thread_resume()函数,让该线程在系统中工作,
6)最后,则是设置kmain线程为idle状态。

 

拓展思考(boot程序加载流程)

  1. 复位键,早期的硬件初始化;
  2. 对CPU处理器架构相关的早期初始化,调用与平台、外设早期初始化的相关函数(如:中断控制器、debug串口等外设)
  3. 调用函数,搭建出一个完整的thread线程系统,并对lk中的定时器进行初始化
  4. 调用thread_create()函数创建出"bootstrap2"线程,并调用thread_resume()函数,让该线程在系统中工作,
  5. 线程设置中断响应请求,完成请求后,设置kmain线程为idle状态,等待下一个中断请求任务。

4. bootstrap2线程分析

1)使用thread_create()函数创建出"bootstrap2"线程后,并使用thread_resume()启动该线程后,接下来将会运行bootstrap2()函数,该函数可以看成是lk启动的第二阶段,它将会继续完成外设的初始化和启动。
2)在kmain函数的最后阶段,在thread线程系统搭建完成后,将会运行下面的代码创建出bootstrap2线程:

thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));

3)此时,将会跳转到bootstrap2函数继续运行,完成整个lk系统启动,bootstarp2函数的定义在文件:
bootable/bootloader/lk/kernel/main.c

对于bootstrap2函数实现的主要功能:
1)arch_init();    /*arch处理器架构第二阶段初始化*/
2)platform_init();    /*platform第二阶段初始化*/
3)target_init();    /*target第二阶段初始化,按键、分区表等*/
4)apps_init();     /*创建多个app线程并运行,aboot_init将加载Linux内核*/

在该函数中,比较重要的是target_init()函数和apps_init()函数,target_init()函数将针对不同的硬件平台进行一些外设初始化,例如,按键、emmc分区等;apps_init()函数则是将整个lk系统要启动的app全部进行启动运行,本质是使用thread_create()函数和thread_resume()函数,创建多个线程并在lk系统中调度线程,比较重要的是aboot_init线程(MTK平台是mt_aboot_init线程),它将会启动Linux内核。

5. apps_init函数分析

apps_init()函数的主要功能是将lk系统中的app线程进行创建和调度,其中比较重要的aboot_init线程,它用于启动Linux内核,apps_init函数的定义在文件:
bootable/bootloader/lk/app/app.c

该函数的定义如下所示:

extern const struct app_descriptor __apps_start[];
extern const struct app_descriptor __apps_end[];
static void start_app(const struct app_descriptor *app);
/* one time setup */
void apps_init(void)
{
	const struct app_descriptor *app;

	/* call all the init routines */
	for (app = __apps_start; app != __apps_end; app++) {    /*遍历所有apps*/
		if (app->init)    /*判断app_descriptor结构的init函数是否存在*/
			app->init(app);    /*如果存在,则调用init函数*/
	}

	/* start any that want to start on boot */
	for (app = __apps_start; app != __apps_end; app++) {
		if (app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) == 0) {
			start_app(app);    /*启动所有要在lk阶段启动的app*/
		}
	}
}

从代码中知道,apps_init函数使用了两个for循环,调用了位于__apps_start与__apps_end之间的函数,对于__apps_start和__apps_end需要去相应的ld链接文件中去寻找,在上面提到的system-onesegment.ld文件中有:

__apps_start =.;
KEEP (*(.apps))
__apps_end=.;
.= ALIGN(4);

可以知道是,调用了所有放在*.apps段中的函数了,在下面的文件中有和*.apps段的相关宏:
bootable/bootloader/lk/include/app.h

宏APP_START和struct app_descriptor结构体定义如下:

/*each app needs to define one of these to define its startup conditions*/
struct app_descriptor {
constchar *name;
app_init init;
app_entry entry;
unsignedintflags;
};
#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
#define APP_END };

因此,可以知道,每个app都有一个app_descriptor结构体进行描述,这些结构体的定义都在.apps段中,接下来,继续搜索使用APP_START宏添加的结构体和函数有什么:
在文件:bootable/bootloader/lk/app/aboot/aboot.c
使用了APP_START宏的定义,如下:

APP_START(aboot)
.init=aboot_init,
APP_END

这就是aboot这个app的定义,aboot_init函数就是要启动的线程,该线程用来启动Linux内核,非常重要,其它的app定义类似。如shell.c、mt_boot.c文件等文件,都导入了app.h,在文本的末尾都使用了APP_START、APP_END宏的定义。
(lk的app包含哪些?全部在app文件夹当中:aboot、mt_boot、shell、clocktests、pcitests、string_tests.c、tests.c等)

在aboot.c、mt_boot.c、shell.c、test.c文件中,都包含了{.init=aboot_init,}该行代码,因此aboot_init、mt_boot_init、shell_init这些在lk阶段都会启动(但从平台的串口日志加了注释标记后,看出aboot_init并没有执行,待后续再继续研究分析)

而在test.c文件中,除了包含{.init=aboot_init,}该行代码,还设置了标志位flags=0;shell.c文件中,包含{.init=shell_init, .entry=shell_entry,},因此,会在执行完shell_init后,再去执行shell_entry函数。

6. aboot_init函数分析

对于aboot_init()函数的定义在文件:
bootable/bootloader/lk/app/aboot/aboot.c

函数的内容如下所示:

void aboot_init(const struct app_descriptor *app)
{
	unsigned reboot_mode = 0;
	unsigned usb_init = 0;
	unsigned sz = 0;

	/* Setup page size information for nand/emmc reads */
	if (target_is_emmc_boot())     /*判断目标板是否是emmc启动*/
	{
		page_size = 2048;
		page_mask = page_size - 1;
	}
	else
	{
		page_size = flash_page_size();   /*读取对应存储介质的page和block大小*/
		page_mask = page_size - 1;
	}

	if(target_use_signed_kernel())
	{
		read_device_info(&device);    /*读取设备的信息*/
	}

可以看出aboot_init主要工作如下:
1、确定page_size大小;
2、从devinfo分区获取devinfo信息;
3、根据条件判断进入不同模式,设置对应标志位boot_into_xxx;
4、进入fastboot模式,初始化fastboot命令等。

Android LK启动流程如下: 1. 开机启动:当用户按下手机的电源键时,系统会进入LK小内核,LK会根据参数值来判断是正常开机启动。 2. recovery启动:当用户按下特定组合键(如音量加键+电源键)时,系统会进入恢复模式,也就是recovery模式。在这种模式下,用户可以进行系统恢复、刷机等操作。 3. fastboot启动:当用户按下特定组合键(如音量减键+电源键)时,系统会进入fastboot模式。在这种模式下,用户可以通过fastboot命令来进行系统的刷机、调试等操作。 4. ffbm启动:ffbm是Fast Factory Boot Mode的缩写,是一种工厂测试模式。在这种模式下,系统会进入工厂测试模式,用于生产线上的测试和调试。 在LK中,关键的函数是apps_init,它负责初始化和运行LK中的app。其中,aboot_init函数负责加载Android Linux内核。apps_init函数会调用所有app的init函数进行初始化,然后根据需要启动一些app。启动app的过程会创建一个线程,并调用相应的app_thread_entry函数来运行app。 总结起来,Android LK启动流程包括正常开机启动、recovery启动、fastboot启动和ffbm启动。在LK中,通过apps_init函数来初始化和运行app,其中aboot_init函数负责加载Android Linux内核。 #### 引用[.reference_title] - *1* *3* [Android lk启动流程](https://blog.youkuaiyun.com/zjy764219923/article/details/107997606)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Android 开发之 ---- bootloader (LK)](https://blog.youkuaiyun.com/jmq_0000/article/details/7378348)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

种树的牧羊人Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值