一、概述
最近在看android framework 的源码,发现网上对最新android framework代码讲解较少,且很多内容存在变更,导致理解起来很吃力,因此打算出一系列关于android framework源码的解析的文章。
本篇文章主要讲解zygote是如何启动的,从内核kernel启动开始,一直贯穿到java层代码,包括以下几部分:
- init 进程的启动时机
- init 进程都做了哪些事情
- zygote进程启动的时机,都做了哪些内容
- java代码是如何运行的
- 简单画了函数之间的调用关系。
二、init 进程的启动时机
1、系统是如何运行的
- 电源和系统启动:当用户按下开机按键时,引导芯片会从预定的地方(Boot ROM)开始执行,加载引导程序BootLootLoader到RAM。
- 启动操作系统:BootLoader 会引导操作系统启动。
- Linux内核启动:swapper(pid=0) 进程启动,用于启动init(pid=1)进程和kthreadd(pid=2)进程。
- 启动进程init:init 进程主要实现的功能是,创建文件、挂载文件、设置selinux(安全策略)、初始化系统属性、挂载额外的系统文件、监听子进程状态,回收僵尸进程、启动系统属性服务、设置commands指令所对应的函数map、解析init.rc、执行rc命令。
2、init 进程的启动
这里从内核启动讲起,而内核的启动是在kernel/common/init/main.c函数在完成的,下面分析一下main.c文件。代码解析如下:
//kernel/common/init/main.c
872 void start_kernel(void)
873 {
//省略初始化相关的代码,只看主线代码
...
//系统还活着,做剩下的初始化内容,其中包括init启动和kthread的启动
1070 arch_call_rest_init();
...
1079 }
823 void __init __weak __noreturn arch_call_rest_init(void)
824 {
//继续向下调用
825 rest_init();
826 }
//nolinline是强制不内联
682 noinline void __ref __noreturn rest_init(void)
683 {
//省略初始化相关的代码,只看主线代码
...
//在用户态线程下运行kernel_init,因为这样可以保证init的进程为0,
//然后在创建kthreadd进程。
693 pid = user_mode_thread(kernel_init, NULL, CLONE_FS);
...
//启动kthreadd进程,主要是创建内核的工作线程kworkder,
//软中断线程ksofttirqd,thermal等内核守护进程,
//这里不是本次的重点,不关注。
706 pid = kernel_thread(kthreadd, NULL, NULL, CLONE_FS | CLONE_FILES);
...
729 }
//继续分析kernel_init进程
1430 static int __ref kernel_init(void *unused)
1431 {
//可以看到从这开始启动了init进程
1495 if (!try_to_run_init_process("/sbin/init") ||
1496 ¦ !try_to_run_init_process("/etc/init") ||
1497 ¦ !try_to_run_init_process("/bin/init") ||
1498 ¦ !try_to_run_init_process("/bin/sh"))
1499 return 0;
1500
1501 panic("No working init found. Try passing init= option to kernel. "
1502 ¦ ¦ "See Linux Documentation/admin-guide/init.rst for guidance.");
1503 }
try_to_run_init_process函数主要是用于启动进程的,下面是对try_to_run_init_process的分析:
//继续分析try_to_run_init_process
1358 static int try_to_run_init_process(const char *init_filename)
1359 {
1360 int ret;
1361
//通过调用run_init_process启动进程
1362 ret = run_init_process(init_filename);
1363
1364 if (ret && ret != -ENOENT) {
1365 pr_err("Starting init: %s exists but couldn't execute it (error %d)\n",
1366 ¦ ¦ init_filename, ret);
1367 }
1368
1369 return ret;
1370 }
下面对run_init_process分析:
//继续分析run_init_process,注意看最后一行。
1343 static int run_init_process(const char *init_filename)
1344 {
1345 const char *const *p;
1346
1347 argv_init[0] = init_filename;
1348 pr_info("Run %s as init process\n", init_filename);
1349 pr_debug(" with arguments:\n");
1350 for (p = argv_init; *p; p++)
1351 pr_debug(" %s\n", *p);
1352 pr_debug(" with environment:\n");
1353 for (p = envp_init; *p; p++)
1354 pr_debug(" %s\n", *p);
//调用系统函数kernel_execve来启动用户空间的进程。
1355 return kernel_execve(init_filename, argv_init, envp_init);
1356 }
经过以上的步骤,init 进程就顺利的启动了,下面我们分析inti进程都做了哪些事情。
三、init 进程都做了哪些事情
根据bp 文件可以轻松的找到init进程源码以及函数入口,bp 文件分析如下:
system/core/init/Android.bp
267 cc_binary {
268 name: "init_second_stage",
269 recovery_available: true,
270 stem: "init",
271 defaults: ["init_defaults"],
272 static_libs: ["libinit"],
//注意看这里,init进程的如何函数在main.cpp中,接下来分析main.cpp源码
273 srcs: ["main.cpp"],
274 symlinks: ["ueventd"],
275 target: {
276 ¦ platform: {
277 ¦ ¦ required: [
278 ¦ ¦ ¦ "init.rc",
279 ¦ ¦ ¦ "ueventd.rc",
280 ¦ ¦ ¦ "e2fsdroid",
281 ¦ ¦ ¦ "extra_free_kbytes",
282 ¦ ¦ ¦ "make_f2fs",
283 ¦ ¦ ¦ "mke2fs",
284 ¦ ¦ ¦ "sload_f2fs",
285 ¦ ¦ ],
286 ¦ },
287 ¦ recovery: {
288 ¦ ¦ cflags: ["-DRECOVERY"],
289 ¦ ¦ exclude_static_libs: [
290 ¦ ¦ ¦ "libxml2",
291 ¦ ¦ ],
292 ¦ ¦ exclude_shared_libs: [
293 ¦ ¦ ¦ "libbinder",
294 ¦ ¦ ¦ "libutils",
295 ¦ ¦ ],
296 ¦ ¦ required: [
297 ¦ ¦ ¦ "init_recovery.rc",
298 ¦ ¦ ¦ "ueventd.rc.recovery",
299 ¦ ¦ ¦ "e2fsdroid.recovery",
300 ¦ ¦ ¦ "make_f2fs.recovery",
301 ¦ ¦ ¦ "mke2fs.recovery",
302 ¦ ¦ ¦ "sload_f2fs.recovery",
303 ¦ ¦ ],
304 ¦ },
305 },
306 visibility: ["//packages/modules/Virtualization/microdroid"],
307 }
废话不多说,下面直接分析system/core/init/main.cpp 代码。这里要格外注意,main.cpp 代码会执行三遍!!!main.cpp 代码会执行三遍!!!main.cpp 代码会执行三遍!!!
1、init 进程第一遍启动
这里是init启动的第一遍,是由上章 try_to_run_init_process(“/sbin/init”) 启动的,启动init时没有传入任何参数!!!分析内容如下:
//system/core/init/main.cpp
53 int main(int argc, char** argv) {
//省略初始化相关的代码,只看主线代码
...
65 if (argc > 1) {
66 ¦ if (!strcmp(argv[1], "subcontext")) {
67 ¦ ¦ android::base::InitLogging(argv, &android::base::KernelLogger);
68 ¦ ¦ const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
69
70 ¦ ¦ return SubcontextMain(argc, argv, &function_map);
71 ¦ }
72
73 ¦ if (!strcmp(argv[1], "selinux_setup")) {
76 ¦ ¦ return SetupSelinux(argv);
77 ¦ }
78
79