init进程是Android系统第一个用户态的进程,init被赋予了很多重要的职责,比如我们熟悉的Zygote孵化器进程就是由init进程启动的。今天我们就来分析init进程的启动过程。

1 init进程启动之前分析

在分析init进程之前,我们先简单说一下init之前的步骤,大概流程如下(源码基于7.0):

1) 按Power键启动电源及系统启动

当按下电源键,引导芯片代码开始从固化在ROM中预定义的地方开始执行,加载引导程序Bootloader到RAM,然后执行引导程序。

2) 引导程序Bootloader

引导程序是Android操作系统被拉起来之前的一个程序,类似于window一样,它的作用就是把系统拉起运行起来。它是针对特定的主板与芯片的。设备制造商要么使用很受欢迎的引导程序比如redboot、uboot、qibootloader或者开发自己的引导程序,它不是Android操作系统的一部分。引导程序是OEM厂商或者运营商加锁和限制的地方。

3)linux内核启动

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

4)init进程启动

init进程是Linux系统中用户空间的第一个进程,进程号固定为1。Kernel启动后,在用户空间启动init进程,并调用init中的main()方法执行init进程的职责。

2 init.cpp的main函数分析

init的入口函数main函数如下:

system/core/init/init.cpp

int main(int argc, char** argv) {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);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) {//1 创建文件所需要的文件目录并且挂载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.open_devnull_stdio();klog_init();klog_set_level(KLOG_NOTICE_LEVEL);NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");if (!is_first_stage) {// Indicate that booting is in progress to background fw loaders, etc.close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//2 初始化属性服务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();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.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) {ERROR("restorecon failed: %s\n", strerror(errno));security_failure();}char* path = argv[0];char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };if (execv(path, args) == -1) 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.NOTICE("Running restorecon...\n");restorecon("/dev");restorecon("/dev/socket");restorecon("/dev/__properties__");restorecon("/property_contexts");restorecon_recursive("/sys");// 创建epoll句柄epoll_fd = epoll_create1(EPOLL_CLOEXEC);if (epoll_fd == -1) {ERROR("epoll_create1 failed: %s\n", strerror(errno));exit(1);}//3 子进程信号处理函数,如果子进程zygote异常退出,init进程会调用该函数设定的信号函数来处理signal_handler_init();// 默认属性导入property_load_boot_defaults();export_oem_lock_status();//4 启动属性服务start_property_service();//5 查看map映射  映射到builtins.cpp中的map函数const BuiltinFunctionMap function_map;Action::set_function_map(&function_map);Parser& parser = Parser::GetInstance();parser.AddSectionParser("service",std::make_unique<ServiceParser>());parser.AddSectionParser("on", std::make_unique<ActionParser>());parser.AddSectionParser("import", std::make_unique<ImportParser>());//6 解析init.rc文件parser.ParseConfig("/init.rc");ActionManager& am = ActionManager::GetInstance();am.QueueEventTrigger("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");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");// Trigger all the boot actions to get us started.am.QueueEventTrigger("init");// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random// wasn't ready immediately after wait_for_coldboot_doneam.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");// 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");}// Run all property triggers based on current state of the properties.am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");while (true) {if (!waiting_for_exec) {//内部执行每个action携带的command对应的执行函数am.ExecuteOneCommand();//重启死去的进程restart_processes();}int timeout = -1;if (process_needs_restart) {timeout = (process_needs_restart - gettime()) * 1000;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;
}

init的入口main函数做了很多事情,我们主要关注以下几点:

  • 注释1处创建文件所需要的文件目录并且挂载
  • property_init(): 初始化属性服务
  • signal_handler_init() : 子进程信号处理函数,如果子进程zygote异常退出,init进程会调用该函数设定的信号函数来处理
  • start_property_service() :启动属性服务
  • parser.ParseConfig("/init.rc"):解析init.rc文件
  • restart_processes(): 重启死去的进程

3 init.rc配置文件解析

init.rc是一个非常重要的配置文件,它是由Android初始化语言(Android Init Language)编写的脚本,它主要包含五种类型语句:Action(Action中包含了一系列的Command)、Commands(init语言中的命令)、Services(由init进程启动的服务)、Options(对服务进行配置的选项)和Import(引入其他配置文件)。

Action

通过触发器trigger,即以on开头的语句来决定执行相应的service的时机,具体有如下时机:

  • on early-init 在初始化早期阶段触发;
  • on init 在初始化阶段触发;
  • on late-init 在初始化晚期阶段触发;
  • on boot/charger 当系统启动/充电时触发,还包含其他情况,此处不一一列举;
  • on property:=: 当属性值满足条件时触发
Service

服务Service,以 service开头,由init进程启动,一般运行在init的一个子进程,所以启动service前需要判断对应的可执行文件是否存在。init生成的子进程,定义在rc文件,其中每一个service在启动时会通过fork方式生成子进程。

例如: service servicemanager /system/bin/servicemanager 代表的是服务名为
servicemanager,服务执行的路径为/system/bin/servicemanager。

Command
  • class_start <service_class_name>: 启动属于同一个class的所有服务;
  • start <service_name>: 启动指定的服务,若已启动则跳过;
  • stop <service_name>: 停止正在运行的服务
  • setprop :设置属性值
  • mkdir :创建指定目录
  • symlink <sym_link>: 创建连接到的<sym_link>符号链接;
  • write : 向文件path中写入字符串;
  • exec: fork并执行,会阻塞init进程直到程序完毕;
  • exprot :设定环境变量;
  • loglevel :设置log级别
Options
  • Options是Service的可选项,与service配合使用
  • disabled: 不随class自动启动,只有根据service名才启动;
  • oneshot: service退出后不再重启;
  • user/group: 设置执行服务的用户/用户组,默认都是root;
  • class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default;
  • onrestart:当服务重启时执行相应命令;
  • socket: 创建名为 /dev/socket/ 的socket
  • critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式
init.rc的配置文件:

system/core/rootdir/init.rc

on initsysclktz 0# Mix device-specific information into the entropy poolcopy /proc/cmdline /dev/urandomcopy /default.prop /dev/urandom# Backward compatibility.symlink /system/etc /etcsymlink /sys/kernel/debug /d
...on boot# basic network initifup lohostname localhostdomainname localdomain# set RLIMIT_NICE to allow priorities from 19 to -20setrlimit 13 40 40
...

接下来我们查看Service类型的语句,在Android 7.0中对init.rc文件进行了拆分,每个服务一个rc文件。我们要分析的zygote服务的启动脚本则在init.zygoteXX.rc中定义,比如init.zygote64.rc,源码如下:

system/core/rootdir/init.zygote64.rc

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-serverclass mainsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdwritepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

上面的意义是指service用于通知init进程创建名字为zygote的进程,执行代码的逻辑在/system/bin/app_process64中,之后的则是传递的参数。

解析service

下面我们解析service,其中会用到两个函数,一个是ParseSection,它会解析service的rc文件,比如上文讲到的init.zygote64.rc,ParseSection函数主要用来搭建service的架子。另一个是ParseLineSection,用于解析子项。代码如下所示

system/core/init/service.cpp


bool ServiceParser::ParseSection(const std::vector<std::string>& args,std::string* err) {if (args.size() < 3) {*err = "services must have a name and a program";return false;}const std::string& name = args[1];if (!IsValidName(name)) {*err = StringPrintf("invalid service name '%s'", name.c_str());return false;}std::vector<std::string> str_args(args.begin() + 2, args.end());// 1 service_ = std::make_unique<Service>(name, "default", str_args);return true;
}bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,const std::string& filename, int line,std::string* err) const {return service_ ? service_->HandleLine(args, err) : false;
}void ServiceParser::EndSection() {if (service_) {//2 ServiceManager::GetInstance().AddService(std::move(service_));}
}void ServiceManager::AddService(std::unique_ptr<Service> service) {Service* old_service = FindServiceByName(service->name());if (old_service) {ERROR("ignored duplicate definition of service '%s'",service->name().c_str());return;}// 3 services_.emplace_back(std::move(service));
}

上面注释1处根据参数创建一个service对象,当解析结束时会调用endSection,然后调用注释2处的addService方法,addService方法中将service添加到services列表中。

init启动zygote

在init.rc中有如下配置信息,在前面init.zygote64.rc中我们看到zygote的class name为main,所以这里class_start main指的是启动zygote。


on nonencrypted# A/B update verifier that marks a successful boot.exec - root -- /system/bin/update_verifier nonencryptedclass_start mainclass_start late_start

class_start是一个COMMAND, 对应的函数是do_class_start,定义在builtins.cpp中:

system/core/init/builtins.cpp

static int do_class_start(const std::vector<std::string>& args) {/* Starting a class does not start services* which are explicitly disabled.  They must* be started individually.*/ServiceManager::GetInstance().//StartIfNotDisabled执行到了service.cpp的StartIfNotDisabled中ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });return 0;
}

StartIfNotDisabled函数进入到service.cpp中

system/core/init/service.cpp

bool Service::StartIfNotDisabled() {if (!(flags_ & SVC_DISABLED)) {return Start();} else {flags_ |= SVC_DISABLED_START;}return true;
}

上面startIfNotDisabled()调用到了start()方法,start方法较长,代码如下:

bool Service::Start() {...//如果Service处于运行状态,则不启动if (flags_ & SVC_RUNNING) {return false;}bool needs_console = (flags_ & SVC_CONSOLE);if (needs_console && !have_console) {ERROR("service '%s' requires console\n", name_.c_str());flags_ |= SVC_DISABLED;return false;}struct stat sb;//判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Serviceif (stat(args_[0].c_str(), &sb) == -1) {ERROR("cannot find '%s' (%s), disabling '%s'\n",args_[0].c_str(), strerror(errno), name_.c_str());flags_ |= SVC_DISABLED;return false;}...NOTICE("Starting service '%s'...\n", name_.c_str());//1 fork函数创建子进程pid_t pid = fork();//2 pid==0 则运行在子进程中if (pid == 0) {umask(077);for (const auto& ei : envvars_) {add_environment(ei.name.c_str(), ei.value.c_str());}...std::vector<char*> strs;for (const auto& s : args_) {strs.push_back(const_cast<char*>(s.c_str()));}strs.push_back(nullptr);//3 execve执行到了app_main.cpp方法if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));}_exit(127);}...NotifyStateChange("running");return true;
}

上面service.cpp中的start()方法首先会判断service的运行状态以及一些运行时需要满足的条件,然后使用fork来创建子进程,其中pid==0说明代码运行在子进程中,然后执行system/bin/app_process.cpp,这样就会进入framework/cmds/app_process/app_main.cpp的main函数,源码如下:

frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {// Older kernels don't understand PR_SET_NO_NEW_PRIVS and return// EINVAL. Don't die on such kernels.if (errno != EINVAL) {LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));return 12;}}AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// Process command line arguments// ignore argv[0]argc--;argv++;...int i;for (i = 0; i < argc; i++) {if (argv[i][0] != '-') {break;}if (argv[i][1] == '-' && argv[i][2] == 0) {++i; // Skip --.break;}runtime.addOption(strdup(argv[i]));}// Parse runtime arguments.  Stop at first unrecognized option.bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;++i;  // Skip unused "parent dir" argument.while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) {zygote = true;niceName = ZYGOTE_NICE_NAME;} else if (strcmp(arg, "--start-system-server") == 0) {startSystemServer = true;} else if (strcmp(arg, "--application") == 0) {application = true;} else if (strncmp(arg, "--nice-name=", 12) == 0) {niceName.setTo(arg + 12);} else if (strncmp(arg, "--", 2) != 0) {className.setTo(arg);break;} else {--i;break;}}...//1 if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) {runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");return 10;}
}

上面在注释1处看到调用了runtime,也就是AppRuntime.cpp的start, 因为AppRuntime.cpp中没有此方法,AndroidRuntime.cpp是AppRuntime.cpp的父类,最终调用的是AndroidRuntime.cpp中的start方法来启动zygote。

4 init进程总结
  • 创建和挂载启动所需要的文件目录
  • 初始化和启动属性服务
  • 解析init.rc配置文件并启动Zygote进程

Android系统启动流程(一) init进程启动过程解析相关推荐

  1. 从源码解析-Android系统启动流程概述 init进程zygote进程SystemServer进程启动流程

    Android系统启动流程 启动流程 Loader Kernel Native Framework Application init进程 启动 rc文件规则 Actions Commands Serv ...

  2. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  3. Android 9 (P)之init进程启动源码分析指南之三

          Android 9 (P)之init进程启动源码分析指南之三 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 An ...

  4. Android系统10 RK3399 init进程启动(三十八) 属性Selinux实战编程

    配套系列教学视频链接: 安卓系列教程之ROM系统开发-百问100ask 说明 系统:Android10.0 设备: FireFly RK3399 (ROC-RK3399-PC-PLUS) 前言 上一节 ...

  5. Android系统启动流程(四)Launcher进程启动过程解析(附带面试题)

    前面我们分析了init进程,zygote进程,SystemServer进程,本篇的Launcher是系统启动流程的最后一个进程. 1 Launcher概述 Launcher进程是一个系统的应用程序,位 ...

  6. 从源码角度看Android系统init进程启动过程

    init进程是Linux系统中用户空间的第一个进程,进程号为1.Kernel启动后,在用户空间启动init进程,并调用/system/core/init.cpp中的main方法执行一些重要的工作. 备 ...

  7. 【Android】系统启动流程(zygote 进程启动流程)

    前言 先上图,大致了解一下 Android 设备点击电源键开机到创建出 system_server 进程的流程, 里面细化的子流程和 system_server 之后发生的事情我将会在后续的文章中详细 ...

  8. Busybox init进程启动过程 (资料收集)

    http://blog.csdn.net/wjs1033/article/details/25740363 busybox的init busybox中的init程序可以完成初始化的基本功能而且去掉了S ...

  9. Android系统启动(四) — Launcher 启动过程

    1 Launcher 概述 系统启动的最后一步是启动一个应用程序来显示系统中已经安装的应用程序,这个应用程序就叫做 Launcher.Launcher 在启动过程中会请求 PackageManager ...

最新文章

  1. 软件项目版本号的命名规则及格式
  2. python 鸭子类型及三大特性
  3. 东北大学计算机 大一物理考试题,东北大学大学物理期末考题及答案Word版
  4. php配置实例,php mailto配置实例
  5. Win10 通过 VirtualBox安装CentOS7操作手册
  6. Spring-@Value
  7. 【实例解析】大型服装集团BI决策系统的分析主题模块
  8. Spring MVC 入门指南(二):@RequestMapping用法详解
  9. MacBook 键盘出现故障,如何修复?
  10. 简单OCX控件的开发
  11. 2017全国计算机二级office题库,2017年计算机二级office题库(附答案)
  12. oracle 的insert into的详解
  13. Ubuntu 安装字体
  14. SQL注入实战 绕WTS-WAF
  15. VS 0x80041FEB
  16. MYS-6ULX-IOT 开发板测评——使用 Yocto 添加软件包
  17. js查找数组元素位置
  18. 忆阻器类脑芯片与人工智能
  19. Mysql主从同步报错解决:Fatal error: The slave I/O thread stops because master and slave have equal..
  20. 肠道-甲状腺轴如何影响健康

热门文章

  1. office2010密钥
  2. (附源码)计算机毕业设计Java城市道路智能停车管理系统
  3. Pushmall共享电商营销推广平台2023年6月升级进度
  4. 送给带眼镜的IT工程师朋友的文章
  5. This file's format is not supported or you don't specify a correct format. 解决办法
  6. orientdb基础命令
  7. Teleport堡垒机安装部署
  8. 如何快速学习CADD计算机辅助药物设计
  9. 1088 三人行 (20 分)——简单分析
  10. echarts地图显示图例对应地点去除标识小圆点