Boot ROM - Boot Read-Only-Memory
在AOSP源码中,system/core/init/main.cpp 里的 main 函数是整个Android用户空间的起点 。它是在Linux内核启动完成后,由内核直接调用的第一个也是唯一一个用户空间进程。
具体时机和调用链条如下:
graph TB
subgraph A["Linux 内核启动流程 (kernel/init/main.c)"]
A1["start_kernel()"] --> A2["arch_call_rest_init()"]
A2 --> A3["rest_init()"]
A3 --> A4["kernel_init()"]
end
subgraph B["关键函数说明"]
A1 --> B1["内核主入口点
完成核心子系统初始化
内存管理、调度器、中断等"] A2 --> B2["体系结构相关的
rest_init调用点"] A3 --> B3["创建关键内核线程
- 启动kernel_init(PID=1)
- 启动kthreadd(PID=2)
- 进入idle循环"] A4 --> B4["内核初始化线程
准备用户空间环境
最终执行用户空间init进程"] end subgraph C["与Android的关系"] A4 --> C1["通过execve执行
/system/bin/init"] C1 --> C2["Android init进程
system/core/init/main.cpp"] C2 --> C3["main()"] C3 --> C4["FirstStageMain()"] end subgraph D["关键函数说明"] C3 --> D1["init进程代码入口"] end style A fill:#e1f5fe style B fill:#f3e5f5 style C fill:#e8f5e8 style A1 fill:#ffeb3b style A4 fill:#ff9800
完成核心子系统初始化
内存管理、调度器、中断等"] A2 --> B2["体系结构相关的
rest_init调用点"] A3 --> B3["创建关键内核线程
- 启动kernel_init(PID=1)
- 启动kthreadd(PID=2)
- 进入idle循环"] A4 --> B4["内核初始化线程
准备用户空间环境
最终执行用户空间init进程"] end subgraph C["与Android的关系"] A4 --> C1["通过execve执行
/system/bin/init"] C1 --> C2["Android init进程
system/core/init/main.cpp"] C2 --> C3["main()"] C3 --> C4["FirstStageMain()"] end subgraph D["关键函数说明"] C3 --> D1["init进程代码入口"] end style A fill:#e1f5fe style B fill:#f3e5f5 style C fill:#e8f5e8 style A1 fill:#ffeb3b style A4 fill:#ff9800
调用时机
- Linux内核启动:设备上电后,Bootloader会加载Linux内核到内存并启动它。
- 内核初始化:内核进行自身的一系列初始化工作,包括驱动、内存管理等。
- 挂载根文件系统 :内核挂载初始RAM磁盘(
initramfs)或者(在Android中常见的)直接挂载系统分区作为根文件系统。 - 启动第一个用户空间进程 :内核在完成所有核心初始化后,会尝试执行一个名为
init的程序。这个程序的路径是在内核编译时指定的,通常就是根文件系统下的/init。
调用过程
这个调用过程是由内核代码直接写死的,其逻辑大致如下:
- 内核入口点 :在内核源码的
init/main.c中,kernel_init函数负责启动第一个用户空间进程。 - 尝试执行 :内核会尝试执行
/sbin/init,/etc/init,/bin/init等路径。 - 回退方案 :如果上述路径都失败了,内核会尝试执行
/bin/sh作为回退。 - 在Android中的情况 :在AOSP编译后,生成的
init可执行文件被直接放置在了根文件系统的根目录下,即/init。因此,内核会成功找到并执行它。
这个 /init 程序,就是由 system/core/init/ 目录下的源码编译而成的。
main.cpp 中的 main 函数
所以,main 函数的调用时机非常明确:在Linux内核完成自身初始化,并准备将控制权交给用户空间的那一刻,由内核直接调用。
它的执行流程可以简化为:
cpp
// system/core/init/main.cpp
int main(int argc, char** argv) {
// 1. 处理内核命令行参数,例如 `androidboot.selinux=permissive`
// 2. 创建必要的目录并挂载文件系统 (如 /dev, /proc, /sys 等)
// 3. 初始化 Kernel Logging (kmsg)
// 4. 进行第一次阶段初始化 (First Stage Mount) - 主要在 recovery 或 ramdisk 环境中
// 5. 如果不是在 First Stage,则执行核心的 init 逻辑
return FirstStageMain(argc, argv); // 或者 SecondStageMain(argv)
}
在现代Android系统中(尤其是使用了动态分区和A/B更新的设备),init的启动被分成了多个阶段:
- First Stage Init :在内核还挂载着初始
rootfs时运行,主要负责挂载真正的系统分区、vendor分区等,并为第二阶段的init设置好环境。 - Second Stage Init :在必要的分区被挂载后,系统会
execv重新执行位于新系统分区上的/system/bin/init,进入第二阶段。这个阶段会建立完整的Android运行环境,包括启动属性服务、解析init.rc脚本、启动守护进程、Zygote等。
总结
| 项目 | 说明 |
|---|---|
| 调用者 | Linux 内核 |
| 调用时机 | 内核完成所有内部初始化之后,准备切换到用户空间时 |
| 调用方式 | 内核通过execve 系统调用执行根文件系统中的 /init 程序 |
| 程序路径 | 在设备根文件系统中为/init,在AOSP源码中位于 system/core/init/ |
因此,init 进程的PID永远是1,它是所有其他用户空间进程的祖先进程。它的 main 函数是Android系统用户空间的绝对起点。
---- 后续讲解FirstStageMain()相关流程----