mirror of
https://github.com/feicong/rom-course.git
synced 2025-11-05 10:23:32 +00:00
第三章内容优化
This commit is contained in:
parent
e8c46d57f4
commit
ef6e55787e
@ -1,12 +1,15 @@
|
||||
# 第三章 认识系统组件
|
||||
|
||||
在上一章的学习中,成功编译了`Android12`以及对应的内核,并且通过多种方式刷入手机。接下来需要先对`Android`源码的根结构有一定的了解,了解结构能有助于更快的定位和分析源码,同时能让开发人员更好的理解`Android`系统。在修改系统时,有些简单的功能(例如`native`中的文件读写,`java`类型的转换`c++`类型等)并不需要我们重新实现,因为这些需求大多数在`Android`系统源码中都有类似的实现,熟练掌握`Android`系统源码,了解系统中常用的那些功能性函数,可以大大的提高定制系统的效率。在源码的学习过程中,最重要的是,**学习系统源码时,碰到问题,要学会跳过,很多时候是需要经历过一遍遍学习和实践后,才能完全理解其中的含义。**
|
||||
在上一章的学习中,成功编译了`Android`12,以及对应的系统内核,并且通过多种方式刷入手机。接下来需要先对`Android`源码的根结构有一定的了解,了解结构能有助于更快的定位和分析源码,同时能让开发人员更好的理解`Android`系统。在修改系统时,有些简单的功能(例如`native`中的文件读写,`java`类型的转换`c++`类型等)并不需要我们重新实现,因为这些需求大多数在`Android`系统源码中都有类似的实现,熟练掌握`Android`系统源码,了解系统中常用的那些功能性函数,可以大大的提高定制系统的效率。在源码的学习过程中。
|
||||
|
||||
**学习系统源码时,碰到问题,要学会暂时记录并跳过,经历过一遍遍学习和实践后,之前遇到的问题可能简单思考便会明白,这不仅节省了时间,也不会在学习过程中逐渐失去信心。**
|
||||
|
||||
|
||||
## 3.1 源码结构介绍
|
||||
|
||||
首先看看`Android`源码根目录下,各个目录的简单介绍。
|
||||
|
||||
1. `art`:该目录在`Android 5.0`中新增加的,主要是实现`Android RunTime(ART)`的目录,它作为`Android 4.4`中的`Dalvik`虚拟机的替代,主要处理`Java`字节码执行。
|
||||
1. `art`:该目录在`Android` 5.0中新增加的,主要是实现`Android RunTime(ART)`的目录,它作为`Android` 4.4中的`Dalvik`虚拟机的替代,主要处理`Java`字节码执行。
|
||||
2. `bionic`:`Android`的`C`库,包含了很多标准的`C`库函数和头文件,还有一些`Android`特有的函数和头文件。
|
||||
3. `build`:该目录包含了编译`Android`源代码所需要的脚本,包括`makefile`文件和一些构建工具。
|
||||
4. `compatibility`:`Android`设备的兼容性测试套件`(CTS)`和兼容性实现`(Compatibility Implementation)`。
|
||||
@ -33,9 +36,10 @@
|
||||
|
||||
在上述目录中,并不需要全部记下,只需要记住几个重点即可。在实践时,为了实现功能,查阅翻读源码时,就会不断加深你对这些目录划分的了解。
|
||||
|
||||
## 3.2 Android启动流程
|
||||
|
||||
`Android`启动流程主要分为四个阶段:`Bootloader`阶段、`Kernel`阶段、`Init`进程阶段和`System Server`启动阶段,首先简单介绍下这几个阶段的启动流程。
|
||||
## 3.2 Android系统启动流程
|
||||
|
||||
`Android`系统启动流程主要分为四个阶段:`Bootloader`阶段、`Kernel`阶段、`Init`进程阶段和`System Server`启动阶段,下面看一下这几个阶段的启动流程。
|
||||
|
||||
1. `Bootloader`阶段: 当手机或平板电脑开机时,首先会执行引导加载程序(`Bootloader`),它会在手机的`ROM`中寻找启动内核(`Kernel`)的镜像文件,并将其加载进`RAM`中。在这个阶段中,`Android`系统并没有完全启动,只是建立了基本的硬件和内核环境。
|
||||
2. `Kernel`阶段: `Kernel`阶段是`Android`启动流程的第二阶段,它主要负责初始化硬件设备、加载驱动程序、设置内存管理等。此外,`Kernel`还会加载`initramfs`,它是一个临时文件系统,包含了`init`程序和一些设备文件。
|
||||
@ -44,15 +48,16 @@
|
||||
|
||||
在开始启动流程代码追踪前,最重要的是不要试图了解所有细节过程,分析代码时要抓住需求重点,然后围绕着需求点来进行深入分析。尽管`Android`源码是一个非常庞大的体系,选择一个方向来熟悉代码,这样就能快速的达成目标,避免深陷代码泥沼。
|
||||
|
||||
|
||||
## 3.3 内核启动
|
||||
|
||||
`Bootloader`其实就是一段程序,这个程序的主要功能就是用来引导系统启动,也称之为引导程序,而这个引导程序是存放在一个只读的寄存器中,从物理地址0开始的一段空间分配给了这个只读存储器来存放引导程序。
|
||||
`Bootloader`其实是一段程序,这个程序的主要功能就是用来引导系统启动,也称之为引导程序,而这个引导程序是存放在一个只读的寄存器中,从物理地址0开始的一段空间分配给了这个只读存储器来存放引导程序。
|
||||
|
||||
`Bootloader`会初始化硬件设备并准备内存空间映射,为启动内核准备环境。然后寻找内核的镜像文件,验证`boot`分区和`recovery`分区的完整性,然后将其加载到内存中,最后开始执行内核。可以通过命令`adb reboot bootloader`直接重启进入引导程序。
|
||||
|
||||
`Bootloader`初始化完成后,会在特定的物理地址处查找`EFI`引导头(`efi_head`)。如果查找到`EFI`引导头,`bootloader`就会加载`EFI`引导头指定的`EFI`引导程序,然后开始执行`EFI`引导程序,以进行后续的`EFI`引导流程。而这个`efi_head`就是`linux`内核最早的入口了。
|
||||
|
||||
这里注意,现在并需要完全看懂内核中的汇编部分代码,主要是为了解执行的流程,所以并不需要你有汇编的功底,只需要能看懂简单的几个指令即可,接下来,打开编译内核源码时的目录,找到文件`android-kernel/private/msm-google/arch/arm64/kernel/head.S`查看汇编代码如下。
|
||||
不做系统引导开发的朋友,并不需要完全看懂内核中的汇编部分代码,了解其执行的流程即可,因此不需要读者有汇编的功底,只需要能看懂简单的几个指令即可。打开编译内核源码时的目录,找到文件`android-kernel/private/msm-google/arch/arm64/kernel/head.S`,查看其汇编代码如下。
|
||||
|
||||
```assembly
|
||||
__HEAD
|
||||
@ -199,7 +204,7 @@ __primary_switched:
|
||||
ENDPROC(__primary_switched)
|
||||
```
|
||||
|
||||
上面能看到最后一个指令就是调用`start_kernel`了,这个函数是内核的入口函数,同时也是`c`语言部分的入口函数。接下来,查看文件`android-kernel/private/msm-google/init/main.c`,可以看到其中大量的`init`初始化各种子系统的函数调用。
|
||||
上面能看到最后一个指令就是调用`start_kernel`了,这个函数是内核的入口函数,同时也是`C`语言部分的入口函数。接下来,查看文件`android-kernel/private/msm-google/init/main.c`,可以看到其中大量的`init`初始化各种子系统的函数调用。
|
||||
|
||||
```c
|
||||
asmlinkage __visible void __init start_kernel(void)
|
||||
@ -279,6 +284,7 @@ static int run_init_process(const char *init_filename)
|
||||
|
||||

|
||||
|
||||
|
||||
## 3.4 Init进程启动
|
||||
|
||||
`init`进程是`Android`系统的第一个进程,它在系统启动之后就被启动,并且一直运行到系统关闭,它是`Android`系统的核心进程,隶属于系统进程,具有最高的权限,所有的其他进程都是它的子进程,它的主要功能有以下几点:
|
||||
@ -671,6 +677,7 @@ Result<void> ImportParser::ParseSection(std::vector<std::string>&& args,
|
||||
|
||||
到这里大致的`init`进程的启动流程相信大家已经有了一定了解。明白`init`的原理后,对于`init.rc`相信大家已经有了简单的印象,接下来将详细展开讲解`init.rc`文件。
|
||||
|
||||
|
||||
## 3.5 init.rc
|
||||
|
||||
`init.rc`是`Android`系统中的一个脚本文件并非配置文件,是一种名为`Android Init Language`的脚本语言写成的文件,当然也可以简单当作是配置文件理解,主要用于启动和管理`Android`上的其他进程对系统进行初始化工作。
|
||||
@ -813,6 +820,7 @@ on property:sys.boot_completed=1
|
||||
|
||||
上面的案例中,我定义了一个`kservice`的服务,使用`/system/bin/app_process`作为进程启动,并设置目标`jar`作为应用的`classpath`,最后设置`jar`文件的入口类`cn.mik.ksvr.kSystemSvr`,最后的`svr`是做为参数传递给`kSystemSvr`中的`main`函数。接下来是当属性`sys.boot_completed`变更为1时表示手机完成引导,执行节点下的命令启动刚刚定义的服务。
|
||||
|
||||
|
||||
## 3.6 Zygote启动
|
||||
|
||||
了解`init.rc`定义的原理后,就可以继续阅读`init.rc`追踪后续的启动流程了。
|
||||
@ -2443,9 +2451,8 @@ public void callApplicationOnCreate(Application app) {
|
||||
}
|
||||
```
|
||||
|
||||
到这里,成功跟踪到最后调用`app`应用的`onCreate`函数,为什么很多人喜欢`hook attach`函数,因为在`Application`创建出来最早先调用了这个函数,该函数是一个较早`hook`时机。下面是结合跟踪的代码总结的简单的启动流程图。
|
||||
到这里,成功跟踪到最后调用`app`应用的`onCreate`函数,为什么很多人喜欢`hook attach`函数,因为在`Application`创建出来最早先调用了这个函数,该函数是一个较早`hook`时机。
|
||||
|
||||
TODO 流程图
|
||||
|
||||
## 3.8 了解Service
|
||||
|
||||
@ -2479,6 +2486,7 @@ TODO 流程图
|
||||
|
||||
还有更多的系统服务为`Android`的运行提供着各模块的基础功能,这里就不展开详细叙述了,当对某一个服务的功能实现感兴趣时,可以顺着启动服务的地方开始跟踪代码,分析实现的逻辑。也可以直接参考系统服务的定义方式来自定义系统服务来提供特殊需求的功能。
|
||||
|
||||
|
||||
## 3.9 了解Framework
|
||||
|
||||
`Framework`指的是软件开发框架,由于系统处于内核中,无法直接对系统的功能进行请求,而是由框架层为开发的顶层应用提供接口调用,从而不必烦恼如何与底层交互,开发框架为开发人员提供各种功能,以及`Android`应用工具的支持来便于创建和管理`Android`应用程序,最终达到让用户能高效开发`Android`应用的目的,以生活中的事务为例,`Framework`就像是一个配套完善的小区,有高效的物业,周边配套有学校、医院、商场,各类设施非常齐全,而用户就像是小区内的业主。
|
||||
@ -2725,7 +2733,7 @@ final class PhoneStateBroadcaster extends CallsManagerListenerBase {
|
||||
|
||||
```
|
||||
|
||||
## 3.9 了解libcore
|
||||
## 3.10 了解libcore
|
||||
|
||||
`libcore`是`Android`平台下的`Java`核心库,主要提供与`Java`语言核心相关的类,如`Object`类、`String`类,`Java`集合类以及输入/输出流等。同时,`libcore`还包括了平台支持库,提供了一些用于`Android`平台特定功能的实现,如`Socket、SSL、File、URI`等类的平台特定实现。在`Android`应用程序开发中,`libcore`库是必不可少的一部分,其提供的类和实现对于开发和调试应用程序都具有非常重要的作用。
|
||||
|
||||
@ -3019,7 +3027,8 @@ allow zygote apk_data_file:dir search;
|
||||
|
||||
在开始了解`Linker`如何加载动态库`so`文件前,需要先对`so`文件有一个简单的了解。
|
||||
|
||||
### 3.12.1 ELF格式
|
||||
|
||||
### 3.12.1 ELF文件格式
|
||||
|
||||
在`Android`中,`so(Shared Object)`动态库是一种是一种基于`ELF`格式`(Executable and Linkable Format)`的可执行文件,它包含已编译的函数和数据,可以在运行时被加载到内存中,并被多个应用程序或共享库使用。
|
||||
|
||||
@ -3145,9 +3154,10 @@ allow zygote apk_data_file:dir search;
|
||||
|
||||

|
||||
|
||||
### 3.12.2 动态加载流程
|
||||
|
||||
`Linker`动态加载是把代码(函数、变量、数据结构等)从动态链接库(`so`文件)中加载到内存中,并建立起代码之间的相互引用关系的过程。在`Android`等操作系统中,`Linker`动态加载主要用于模块化开发,将程序分为多个独立的模块,以便于代码的管理和维护。下面是`Linker`动态加载的主要步骤:
|
||||
### 3.12.2 动态库加载流程
|
||||
|
||||
`Linker`动态库加载是把代码(函数、变量、数据结构等)从动态链接库(`so`文件)中加载到内存中,并建立起代码之间的相互引用关系的过程。在`Android`等操作系统中,`Linker`动态加载主要用于模块化开发,将程序分为多个独立的模块,以便于代码的管理和维护。下面是`Linker`动态加载的主要步骤:
|
||||
|
||||
1. 根据系统的运行时需求,将需要的库文件加载进内存中,实现代码重用和共享。此时,`Linker`会执行一些特定的逻辑,如依赖优化、`so`文件版本检查等。
|
||||
2. 在进行动态链接的过程中,`Linker`会为每个库和每个函数生成全局唯一的标识符,以确定代码所在的地址。这个标识符会在编译过程中嵌入到库文件的头部,并且保存到动态链接库的符号表中。
|
||||
@ -3637,4 +3647,6 @@ bool read(const char* realpath, off64_t file_size) {
|
||||
|
||||
## 小结
|
||||
|
||||
TODO
|
||||
系统定制无论做怎样的修改,都要明白其原理,本章内容作为系统开发的内功心法,需要读者花费一些时间来吸收,在后面的内容的展开过程中,也可以随时重温本章内容,加深印象。
|
||||
|
||||
从设备开机到系统启动完成,整个启动链上涉及到的核心组件都在本节中进行了介绍。本节中介绍的系统组件,都是定制系统可能需要修改的地方。其中,Service与Framework的修改是最常见的,美化与安全定制都离不开它,读者朋友们可以重点阅读它们的代码来深入研究。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user