Android系统启动流程-Android的Init进程


一、Linux内核启动

1、启动电源以及系统启动
当电源按下时引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序Bootloader到RAM,然后执行。

2、引导程序Bootloader
Bootloader是在操作系统运行之前运行的一段程序,它是运行的第一个程序,因此它是针对特定的主板与芯片的。它不是Android操作系统的一部分,而是OEM厂商或者运营商加锁和限制的地方。它将系统的软硬件环境带到一个合适的状态,为运行操作系统做好准备。类似于电脑上的BIOS,其目的就是要把OS拉起来运行。Booloader有三种模式:
1)开机后按组合键启动到fastboot模式,即命令或SD卡烧写模式,不加载内核及文件系统,可以通过数据线与电脑连接,然后在电脑上执行一些命令,如刷系统镜像到手机上。
2)开机后按组合键启动到recovery模式,加载recovery.img,recovery.img包含内核、部分系统文件,可以读取sdcard中的update.zip进行刷机,也可以清楚cache和用户数据。
3)开机按Power正常启动系统,加载boot.img。boot.img包含内核、基本文件系统,用于正常启动手机。
Bootloader做的事情主要有:初始化CPU时钟、内存、串口等;设置Linux启动参数;加载Linux各种内核镜像到SDRAM中。

3、Linux内核启动
内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。

4、init进程启动
Init进程是一个由内核启动的用户级进程,可以把它看做是root进程或者说所有进程的父进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。init进程有两个责任,一是挂载目录,比如/sys,/dev,/proc,二是运行init.rc脚本。

二、Init进程
1、init入口函数:system/core/init/init.cpp main

int main(int argc, char** argv) {
// (1)首先检查启动程序的文件名
// 如果文件名为:ueventd,执行守护进程ueventd的主函数ueventd_main。
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
// 大致同上
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
// (2)缺省情况下一个进程创建出的文件和文件夹属性是022,使用umask()函数能设置文件属性
// 的掩码。参数为0意味着进程创建的文件属性是0777。
// Clear the umask.
umask(0);

add_environment("PATH", _PATH_DEFPATH);


bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);


Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
if (is_first_stage) {
// (3)创建一些基本的目录;同时把一些文件系统Mount到相应的目录。
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");//基于内存的文件系统
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);//虚拟终端文件系统
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));//基于内存的虚拟文件系统,内部数据结构的接口
mount("sysfs", "/sys", "sysfs", 0, NULL);//用于把系统的设备和总线按层次组织起来
}

// We must have some place other than / to create the device nodes for
// kmsg and null, otherwise we won't be able to remount / read-only
// later on. Now that tmpfs is mounted on /dev, we can actually talk
// to the outside world.
// (4)把标准输入、标准输出和标准错误重定向到空设备文件/dev/_null_    
open_devnull_stdio();
// (5)创建节点/dev/_msg_,这样init进程可以使用kernel的log系统来输出log
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);


NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");

if (!is_first_stage) {
// (6)在/dev目录下创建一个空文件.booting表示初始化正在进行,is_booting函数将依
// 靠.booting来判断进程是否处于初始化中
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
// (7)初始化Android属性系统。创建一个共享区域来存储属性值。
property_init();


// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
// (8)解析kernel的启动参数,通常放在/proc/cmdline中,利用解析结果参数中的值设置几个属性值
process_kernel_cmdline();


// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();
}

// Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
// (9)初始化SElinux
selinux_initialize(is_first_stage);


// If we're in the kernel domain, re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (is_first_stage) {
if (restorecon("/init") == -1) { //按SELinux要求,重新设定/init文件属性
ERROR("restorecon failed: %s\n", strerror(errno));
security_failure();
}
char* path = argv[0];
char* args[] = { path, const_cast("--second-stage"), nullptr }; //设置参数--second-stage
if (execv(path, args) == -1) { // 执行init进程,重新进入main函数
ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
security_failure();
}
}


// 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.
// 这些目录必须在初始策略加载之前创建,因此需要将其安全上下文恢复到适当的值。 这必须在/ dev由ueventd填充之前发生。
NOTICE("Running restorecon...\n");
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon("/property_contexts");
restorecon_recursive("/sys");


epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
ERROR("epoll_create1 failed: %s\n", strerror(errno));
exit(1);
}
// (10)装载子进程信号处理器
signal_handler_init();
// (11)解析设备根目录下的default.prop文件,把文件中定义的属性值都出来设置到属性系统中
property_load_boot_defaults();
export_oem_lock_status();
start_property_service(); //启动属性服务
#ifdef BOOT_TRACE
if (boot_trace) {
ERROR("enable boot systrace...");
property_set("debug.atrace.tags.enableflags", "0x3ffffe");
}
#endif
// (12)匹配命令和函数之间的对应关系
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
// (13)解析init.rc
Parser& parser = Parser::GetInstance();// 构造解析文件用的parser对象
// 增加ServiceParser为一个section,对应name为service
parser.AddSectionParser("service",std::make_unique());
// 增加ActionParser为一个section,对应name为on
parser.AddSectionParser("on", std::make_unique());
// 增加ImportParser为一个section,对应name为import
parser.AddSectionParser("import", std::make_unique());
// 开始解析,函数源码在system/core/init/ init_parser.cpp中
parser.ParseConfig("/init.rc");
// (14)向执行队列中添加action
// 获取ActionManager实例,需要通过ActionManager对命令执行顺序进行控制
ActionManager& am = ActionManager::GetInstance();
// init执行命令触发器
am.QueueEventTrigger("early-init"); // 添加触发器early-init,执行on early-init内容


// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done"); //等待冷插拔设备初始化完成
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); //从硬件RNG的设备文件/dev/hw_random中读取512字节并写到Linux RNG的设备文件/dev/urandom中。
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(keychord_init_action, "keychord_init"); //初始化组合键监听模块
am.QueueBuiltinAction(console_init_action, "console_init"); //在屏幕上显示Android字样的logo


// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init"); // 添加触发器init,执行on init内容,主要包括创建/挂载一些目录,以及symlink等


// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");//重复以防冷插拔设备初始化后/dev/hw_random或者/dev/random未就绪


// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = property_get("ro.bootmode");
if (bootmode == "charger") { // 判断是否为充电模式
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init"); // 非充电模式添加触发器late-init
}


// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");//检查Action列表中通过修改属性来触发的Action,查看相关的属性值是否已经设置,如果已经设置,则将该Action加入到执行列表中。


/* 360OS begin */
get_cpu_id();
/* 360OS end */

// (15)处理添加到运行队列中的事件
while (true) {
if (!waiting_for_exec) {
am.ExecuteOneCommand(); //依次执行每个action中携带的命令
restart_processes(); //重启一些挂掉的进程
}


int timeout = -1;
if (process_needs_restart) { //还有服务进程需要重启
timeout = (process_needs_restart - gettime()) * 1000; //把timeout值设为下次启动服务的时间
if (timeout < 0)
timeout = 0;
}


if (am.HasMoreCommands()) { //命令列表中还有命令
timeout = 0;
}


bootchart_sample(&timeout);


epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}


return 0;
}


2、解析init.rc
init.cpp的main函数中,(13)步对init.rc做了解析,这是我们比较关注的东西。
init.rc(/system/core/rootdir/init.rc)是什么?
init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,在Android中有特定的格式以及规则。
Android初始化语言由四大类型的声明组成,即Actions(动作)、Commands(命令)、Services(服务)、以及Options(选项)。所有这些都以行为单位,各种符号则用空格隔开。

1)init.rc(/system/core/rootdir/init.rc):

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
...

导入一些配置文件,zygote的.rc文件有多个。主要就是来区分不同的平台(32、64及64_32)。 init.rc文件进行了拆分,每个服务一个rc文件。

2)init.rc(/system/core/rootdir/init.rc):
...
on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000


# Disable sysrq from keyboard
write /proc/sys/kernel/sysrq 0


# Set the security context of /adb_keys if present.
restorecon /adb_keys


# Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
mkdir /mnt 0775 root system


# Set the security context of /postinstall if present.
restorecon /postinstall


start ueventd
...

on early-init为action类型语句,其一般格式为:
on [&& ]*     //设置触发器  
  
      //动作触发之后要执行的命令  

3)init.zygote64.rc(/system/core/rootdir/init.zygote64.rc):
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

service zygote ...为service类型语句,其一般格式为:
service [ ]*   //<执行程序路径><传递参数>