Android init进程——源码分析
16lz
2021-01-23
概述
Android本质上是一个基于Linux内核的开源操作系统,与我现在用的Ubuntu系统类似,但是所有的Android设备都是运行在ARM处理器(ARM源自进阶精简指令集机器,源自ARM架构)上,而像Ubuntu操作系统是x86(x86是一系列的基于intel 8086 CPU计算机微处理器指令集架构)系统。不过既然Android也是基于Linux内核的系统,那么基本的启动过程也应该符合Linux的规则。下图基本描述了当你按下电源开关后Android设备的执行步骤:一个完整的Linux系统首先会将一个Linux内核装载到内存,也就是编译Linux内核源代码生产的bzImage文件,对于为Android优化的LInux内核源代码会生成zImage文件。该文件就是Linux内核的二进制版本。由于zImage在内核空间运行,而我们平常使用的软件都是在应用空间运行,内核空间和应用空间是不能直接通过内存地址级别访问的,所以就需要建立某种通讯机制。目前Linux有很多通讯机制可以在用户空间和内核空间之间交互,例如设备驱动文件(位于/dev目录中)、内存文件(/proc、/sys目录等)。Linux的重要特征之一就是一切都是以文件的形式存在,例如,一个设备通常与一个或多个设备文件对应。这些与内核空间交互的文件都在用户空间,所以在Linux内核装载完,需要首先建立这些文件所在的目录,而完成这些工作的程序就是init进程。在分析init的核心代码之前,可以初步了解init的主要做了如下工作:
- Android系统有很多属性,init提供了一个property_service(属性服务)来管理它们。
- 处理配置文件的命令(主要是init.rc文件),包括处理各种Action。
init进程源码
源码位于:system/core/init/目录,本文将基于Android4.4.4_r1版本进行分析。其中init.c是init的主文件,由于init是命令行程序,所以分析init.c需要从main函数入手,我对main函数做了注释,代码如下:int main(int argc, char **argv){ int fd_count = 0; struct pollfd ufds[4]; char *tmpdev; char* debuggable; char tmp[32]; int property_set_fd_init = 0; int signal_fd_init = 0; int keychord_fd_init = 0; bool is_charger = false; if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), "watchdogd")) return watchdogd_main(argc, argv); /* clear the umask */ umask(0); // 创建用户空间的目录,例如/dev,/proc,/sys等。 mkdir("/dev", 0755); mkdir("/proc", 0755); mkdir("/sys", 0755); mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); // 检测/dev/.booting文件是否可读写和可创建。 close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000)); // 将标准输入、输出、错误输出重定向到/dev/__null__。 open_devnull_stdio(); // 将init的日志输出设备设置为/dev/__kmsg__。 klog_init(); // 初始化和属性相关的资源 property_init(); get_hardware_name(hardware, &revision); // 处理内核命令行 process_kernel_cmdline(); union selinux_callback cb; cb.func_log = klog_write; selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_initialize(); /* These directories were necessarily created before initial policy load * and therefore need their security context restored to the proper value. * This must happen before /dev is populated by ueventd. */ restorecon("/dev"); restorecon("/dev/socket"); restorecon("/dev/__properties__"); restorecon_recursive("/sys"); is_charger = !strcmp(bootmode, "charger"); INFO("property init\n"); if (!is_charger) property_load_boot_defaults(); INFO("reading config file\n"); // 分析/init.rc文件的内容 init_parse_config_file("/init.rc"); // 解析完init.rc配置文件后,会得到一系列的Action动作。 // init将动作的执行时间划分为四个阶段:early-init,init,early-boot,boot action_for_each_trigger("early-init", action_add_queue_tail); queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(console_init_action, "console_init"); /* execute all the boot actions to get us started */ action_for_each_trigger("init", action_add_queue_tail); /* skip mounting filesystems in charger mode */ if (!is_charger) { action_for_each_trigger("early-fs", action_add_queue_tail); action_for_each_trigger("fs", action_add_queue_tail); action_for_each_trigger("post-fs", action_add_queue_tail); action_for_each_trigger("post-fs-data", action_add_queue_tail); } /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random * wasn't ready immediately after wait_for_coldboot_done */ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(check_startup_action, "check_startup"); if (is_charger) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail); } /* run all property triggers based on current state of the properties */ queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");#if BOOTCHART queue_builtin_action(bootchart_init_action, "bootchart_init");#endif // 进入无限循环,建立init的子进程(init是所有进程的父进程) for(;;) { int nr, i, timeout = -1; execute_one_command(); restart_processes(); if (!property_set_fd_init && get_property_set_fd() > 0) { ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; property_set_fd_init = 1; } if (!signal_fd_init && get_signal_fd() > 0) { ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; signal_fd_init = 1; } if (!keychord_fd_init && get_keychord_fd() > 0) { ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; keychord_fd_init = 1; } if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } if (!action_queue_empty() || cur_action) timeout = 0;#if BOOTCHART if (bootchart_count > 0) { if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) timeout = BOOTCHART_POLLING_MS; if (bootchart_step() < 0 || --bootchart_count == 0) { bootchart_finish(); bootchart_count = 0; } }#endif nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; for (i = 0; i < fd_count; i++) { if (ufds[i].revents == POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); else if (ufds[i].fd == get_signal_fd()) handle_signal(); } } } return 0;}
推荐阅读链接
经过了一段时间对Android系统的学习,我发现init进程主要完成了两大块任务:
- 解析init.rc配置文件。
- 属性服务。
针对这两大块任务,我花费精力用Markdown语法重写了两篇文章,建议阅读:
1.Android init进程——属性服务
2.Android init进程——解析配置文件
更多相关文章
- Android 上传头像(文件)到服务器
- Android 进程保活
- Android代码混淆配置(Proguard文件解析)
- 升级Android SDK后ADT找不到adb.exe文件的解决办法
- 浅入浅出 Android 安全:第二章 Android Linux 内核层安全
- android 文件系统分析
- android下解析xml文件遇到中文问题
- Android开发5――文件读写
- android 获取uri的正确文件路径的办法