Android
设备的启动必须经历三个阶段:Boot Loader
、Linux Kernel
和 Android
系统服务。严格来说,Android
系统实际是运行在 Linux
内核之上的一系列“服务进程”,而这些服务进程的“老祖宗”就是 init
进程。
Boot Loader
是在操作系统内核运行之前的一段小程序。通过这段小程序,可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
init
进程是 Android
系统中用户空间的第一个进程,PID
(进程号)为 1
,是 Android
系统启动流程中一个关键的步骤,作为第一个进程,被赋予了很多极其重要的工作职责,它通过解析 init.rc
文件来构建出系统的初始运行状态,即Android
系统服务大多是在 init.rc
脚本文件中有描述并按照一定的条件启动。
init
进程启动做了很多工作,总的来说主要做了以下三件事:
- 创建(
mkdir
)和挂载(mount
)启动所需的文件目录; - 初始化和启动属性服务(
property service
); - 解析
init.rc
配置文件并启动Zygote
进程;
init
进程是由多个源文件共同组成的,这些文件位于源码目录 system/core/init
中。
在 Linux
内核加载完成后,首先会在系统文件中寻找 init.rc
文件(/system/core/rootdir/init.rc
),并启动 init
进程。Android 10
对 init
进程的入口函数进行了调整,不再是之前的 /system/core/init/init.cpp
的 main
函数,**而是换成了system/core/init/main.cpp
的 main
函数,这样做的目的是把各个阶段的工作分离开,使得代码逻辑更加简明,**代码如下所示:
// /system/core/init/main.cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv); // 1
}
第一阶段:创建和挂载启动所需要的文件目录
注释 1
处的代码是 init
进程的第一阶段,其实现在 first_state_init.cpp
文件中(/system/core/init/first_stage_init.cpp
),代码如下所示:
// /system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now(); // 用于记录启动时间
umask(0); // Clear the umask. 清理umask
CHECKCALL(clearenv());
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
/*---------- 创建和挂载启动所需的文件目录 ----------*/
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755")); // 挂载 tmpfs 文件
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL)); // 挂载 devpts 文件系统
#define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC))); // 挂载 proc 文件系统
#undef MAKE_STR
CHECKCALL(chmod("/proc/cmdline", 0440)); // 8.0 新增,收紧了 cmdline 目录的权限
gid_t groups[] = {AID_READPROC}; // 8.0 新增,增加了个用户组
CHECKCALL(setgroups(arraysize(groups), groups));
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL)); // 挂载的 sysfs 文件系统
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL)); // 8.0 新增
// 提前创建了 kmsg 设备节点文件,用于输出 log 信息
CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
if constexpr (WORLD_WRITABLE_KMSG) {
CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
}
CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
CHECKCALL(mkdir("/mnt/vendor", 0755)); // 创建可供读写的 vendor 目录
CHECKCALL(mkdir("/mnt/product", 0755)); // 创建可供读写的 product 目录
CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
#undef CHECKCALL
SetStdioToDevNull(argv);
// 初始化 Kernel Log 系统,这样就可以从外界获取 Kernel 日志
InitKernelLogging(argv);
...
}
由上面代码可知, init
进程的第一个阶段主要是用来创建和挂载启动所需的文件目录,其中挂载了 tmpfs
、devpts
、proc
、sysfs
和 selinuxfs
共 5
种文件系统,这些都是系统运行时目录,也就是说,只有在系统运行时才会存在,系统停止时会消失。
四类文件系统:
-
tmpfs
:一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中,如果将tmpfs
文件系统卸载后,那么其下的所有内容将不复存在。tmpfs
既可以使用RAM
,也可以使用交换分区,会根据实际需要改变大小。tmpfs
的速度非常惊人,毕竟它是驻留在RAM
中的,即使用了交换分区,性能仍然非常卓越。由于tmpfs
是驻留在RAM
的,因此它的内容是不持久的。断电后,tmpfs
的内容就消失了,这也是被称作tmpfs
的根本原因; -
devpts
:为伪终端提供了一个标准接口,它的标准挂接点是/dev/pts
。只要pty
的主复合设备/dev/ptmx
被打开,就会在/dev/pts
下动态的创建一个新的pty
设备文件; -
proc
:一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统信息,同时也能够在运行时修改特定的内核参数; -
sysfs
:与proc
文件系统类似,也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在/sys
目录下。sysfs
文件系统是Linux 2.6
内核引入的,它把连接在系统上的设备和总线组织成一个分级的文件,使得它们可以在用户空间存取;
第二阶段:初始化并启动属性服务
// system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {
...
// 1 初始化属性服务
property_init();
...
// 创建 epoll 句柄
Epoll epoll;
if (auto result = epoll.Open(); !result) {
PLOG(FATAL) << result.error();
}
// 2 创建 Handler 处理子进程的终止信号,如果子进程(Zygote)异常退出,init进程会调用该函数中设定的信号处理函数来进行处理
InstallSignalFdHandler(&epoll);
property_load_boot_defaults(load_debug_prop);
UmountDebugRamdisk();
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
// 3 启动属性服务
StartPropertyService(&epoll);
...
}
在注释 1
处调用 property_init()
函数来对属性进行初始化,并在注释 3
处调用 StartPropertyService
函数启动属性服务。
在注释 2
处调用 InstallSignalFdHandler
函数用于设置子进程信号处理函数,主要是用来防止 init
进程的子进程成为僵尸进程。为了防止僵尸进程的出现,系统会在子进程暂停或者终止的时候发出 SIGCHLD
信号,而 InstallSignalFdHandler
函数是用来接收 SIGCHLD
信号的(其内部只处理进程终止的 SIGCHLD
信号)。
僵尸进程
僵尸进程与危害:在 UNIX/Linux
中,父进程使用 fork
创建子进程,在子进程终止后,如果无法通知到父进程,这时虽然子进程已经退出了,但是在系统进程表中还为它保留了一定的信息(比如进程号、退出状态、运行时间等),这个进程就被称为是僵尸进程。系统进程表是一项有限资源,如果系统进程表被僵尸进程耗尽的话,系统就可能无法创建新的进程了。
属性服务
Window
平台上有一个注册表管理器,注册表的内容采取键值对的形式来记录用户、软件的一些使用信息。即使系统或者软件重启,其还是能够根据之前注册表中的记录,进行相应的初始化工作。Android
也提供了类似的机制,叫做属性服务。
init
进程启动时会启动属性服务,并为其分配内存,用来存储这些属性,如果需要这些属性直接读取就可以了, 在 init.cpp
的 main
函数中与属性服务相关的代码有以下两行:
// /system/core/init/init.cpp
property_init();
StartPropertyService(&epoll);
这两行代码用来初始化属性服务配置并启动属性服务。
1 属性服务初始化与启动
property_init
函数的具体实现如下所示:
// /system/core/init/property_service.cpp
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
if (__system_property_area_init()) {
LOG(FATAL) << "Failed to initialize property area";
}
if (!property_info_area.LoadDefaultPath()) {
LOG(FATAL) << "Failed to load serialized property info file";
}
}
注释 1
处的 __system_property_area_init()
函数用来初始化属性内存区域。接下来看 StartPropertyService
函数的代码:
// /system/core/init/property_service.cpp
void StartPropertyService(Epoll* epoll) {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr); // 1
if (property_set_fd == -1) {
PLOG(FATAL) << "start_property_service socket creation failed";
}
listen(property_set_fd, 8); // 2
if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) { // 3
PLOG(FATAL) << result.error();
}
}
在注释 1
处创建非阻塞的 Socket
。在注释 2
处调用 listen
函数对 property_set_fd
进行监听,这样创建的 Socket
就成为 server
,也就是属性服务;listen
函数的的第二个参数设置为 8
,意味着属性服务最多可以同时为 8
个试图设置属性的用户提供服务。注释 3
处的代码将 property_set_fd
放入了 epoll
中,用 epoll
来监听 property_set_fd
:当 property_set_fd
中有数据到来时,init
进程将调用 RegisterHandler
进行处理。
在 Linux
新的内核中,epoll
用来替换 select
,epoll
是 Linux
内核为处理大批量文件描述符而做了改进的 poll
,是 Linux
下多路复用 I/O
接口 select/poll
的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统 CPU
利用率。epoll
内部用于保存时间的数据类型是红黑树,查找速度快,select
采用的数组保存信息,查找速度很慢,只有当等待少量文件描述符时,epoll
和 select
的效率才会差不多。
2 服务处理客户端请求
从上面可以知道,属性服务接收到客户端的请求时,会调用 handle_property_set_fd
函数进行处理:
// /system/core/init/property_service.cpp
static void handle_property_set_fd() {
switch (cmd) {
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];
// 如果Socket读取不到属性数据则返回
if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
!socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
return;
}
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
const auto& cr = socket.cred();
std::string error;
uint32_t result =
HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error); // 1
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
<< "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
<< error;
}
break;
}
...
}
注释 1
处对 handle_property_set_fd
函数做了进一步的封装处理,如下所示:
// /system/core/init/property_service.cpp
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
...
// 如果属性名称以 `ctl.` 开头,说明是控制属性
if (StartsWith(name, "ctl.")) { // 1
// 设置控制属性
HandleControlMessage(name.c_str() + 4, value, cr.pid); // 2
return PROP_SUCCESS;
}
// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
if (name == "sys.powerctl") {
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
std::string process_cmdline;
std::string process_log_string;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process
// path.
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
}
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
}
if (name == "selinux.restorecon_recursive") {
return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
}
return PropertySet(name, value, error); // 3
}
系统属性分为两种类型,一种是普通属性,另一种是控制属性,控制属性用来执行一些命令,比如开机的动画就使用了这种属性。因此,HandlePropertySet
分为两个处理分之,一部分处理控制属性,另一部分用于处理普通属性,这里只分析普通属性。如果注释 1
处的属性名称以 ctl.
开头,说明是控制属性,如果客户端权限满足,则会调用 HandleControlMessage
函数来修改控制属性。如果是普通属性,则会在客户端权限妈祖的条件下调用注释 3
处的 PropertySet
来对普通属性进行修改,如下所示:
// /system/core/init/property_service.cpp
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
size_t valuelen = value.size();
// 判断是否合法
if (!IsLegalPropertyName(name)) {
*error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
...
// 从属性存储空间查找该属性
prop_info* pi = (prop_info*) __system_property_find(name.c_str()); // 1
// 如果属性存在
if (pi != nullptr) {
// ro.* properties are actually "write-once". 如果属性名称以"ro."开头,则表示只读,不能修改,直接返回
if (StartsWith(name, "ro.")) {
*error = "Read-only property was already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
// 如果属性存在,就更新属性值
__system_property_update(pi, value.c_str(), valuelen);
} else {
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
*error = "__system_property_add failed";
return PROP_ERROR_SET_FAILED;
}
}
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
// 属性名称以"persist."开头的处理部分
if (persistent_properties_loaded && StartsWith(name, "persist.")) {
WritePersistentProperty(name, value);
}
property_changed(name, value);
return PROP_SUCCESS;
}
PropertySet
函数主要对普通属性进行修改,首先要判断该属性是否合法,如果合法就在注释 1
处从属性存储空间中查找该属性,如果属性存在,就更新属性值,否则就添加该属性。另外,还对名称以 ro.
persist.
开头的属性进行了相应的处理。
第三阶段:解析 init.rc
配置文件
// /system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
if (!parser.ParseConfig("/product_services/etc/init")) {
late_import_paths.emplace_back("/product_services/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
init.rc
是一个非常重要的配置文件,它是由 Android
初始化语言(Android Init Language
) 编写的脚本,这种语言主要包含 5
种类型语句:Action(行为)
、Command(命令)
、Service(服务)
、Option(选项)
和 Import(引入)
。
init.rc
文件大致分为两大部分,一部分是以 on
关键字开头的“动作列表”(action list
),另一部分是以 service
关键字开头的“服务列表”(service list
)。
动作列表用于创建所需目录以及为某些特定文件指定权限,服务列表用来记录 init
进程需要启动的一些子进程。 如下所示:
// /system/core/rootdir/init.rc
on init
sysclktz 0
copy /proc/cmdline /dev/urandom
copy /system/etc/prop.default /dev/urandom
...
on boot
# basic network init
ifup lo
hostname localhost
domainname localdomain
...
on init
和 on boot
是 Action
类型的语句,它的格式如下所示:
on <triggger> [&& <trigger>]* // 设置触发器
<command>
<command> // 动作触发之后要执行的命令
对于如何创建 Zygoet
,主要查看 Service
类型语句,它的格式如下所示:
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
Service
类型的语句格式:
service <name> <pathname> [<argument>]* // <service的名字><执行程序路径><传递参数>
<option> // option 是 service 的修饰词,影响什么时候、如何启动 service
<option>
...
需要注意的是,在 Android 8.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-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary 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
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
根据 Service
类型语句的格式来分析上面代码的意思。Service
用于通知 init
进程创建名为 Zygote
的进程,这个进程的执行路径为 /system/bin/app_process64
,后面的代码是要传给 app_process64
的参数。class main
指的是 Zygote
的 classname
为 main
。
解析 Service
类型语句
init.rc
中的 Action
类型语句和 Service
类型语句都有相应的类来进行解析,Action
类型语句采用 ActionParser
来进行解析,Service
类型语句采用 ServiceParser
来进行解析。因为这里主要分析 Zygote
,所以只介绍 ServiceParser
。
ServiceParser
的实现代码在 system/core/init/service.cpp
中。接下来查看 ServiceParser
是如何解析上面提到的 Service
类型的语句的,会用到两个函数:一个是 ParseSection
,它会解析 Service
的 .rc
文件,比如,上文讲到的 init.zygote64.rc
,ParseSection
函数主要用来搭建 Service
的架子;另一个是 ParseLineSection
,永远解析子项。代码如下所示:
// system/core/init/service.cpp
Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
if (args.size() < 3) { // 判断Service是否有name与可执行程序
return Error() << "services must have a name and a program";
}
const std::string& name = args[1];
if (!IsValidName(name)) { // 检查Service的name是否有效
return Error() << "invalid service name '" << name << "'";
}
filename_ = filename;
Subcontext* restart_action_subcontext = nullptr;
if (subcontexts_) {
for (auto& subcontext : *subcontexts_) {
if (StartsWith(filename, subcontext.path_prefix())) {
restart_action_subcontext = &subcontext;
break;
}
}
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
if (str_args[0] == "/sbin/watchdogd") {
str_args[0] = "/system/bin/watchdogd";
}
}
service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args); // 1
return Success();
}
Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
return service_ ? service_->ParseLine(std::move(args)) : Success();
}
注释 1
处,根据参数构造出一个 Service
对象。在解析完数据后会调用 EndSection
函数:
// system/core/init/service.cpp
Result<Success> ServiceParser::EndSection() {
if (service_) {
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {
return Error() << "ignored duplicate definition of service '" << service_->name()
<< "'";
}
if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) {
return Error() << "cannot update a non-updatable service '" << service_->name()
<< "' with a config in APEX";
}
service_list_->RemoveService(*old_service);
old_service = nullptr;
}
service_list_->AddService(std::move(service_));
}
return Success();
}
EndSection
函数中回调用 ServiceManager.AddService
函数,以下是 AddService
函数:
// system/core/init/service.cpp
void ServiceList::AddService(std::unique_ptr<Service> service) {
services_.emplace_back(std::move(service)); // 1
}
注释 1
处的代码将 Service
对象加入到 Service
链表中。Service
解析过程总体来讲就是根据参数创建出 Service
对象,然后根据选项域的内容填充 Service
对象,最后将 Service
对象加入 vector
类型的 Service
链表中。
第四阶段:启动 Zygote
进程
了解了 Service
解析过程后,接着就是 init
进程如何启动 Service
,这里主要讲解启动 Zygote
这个 Service
。在 Zygote
的启动脚本中,可以知道 Zygote
的 classname
为 main
。在 init.rc
中有如下配置代码:
// /system/core/rootdir/init.rc
on nonencrypted
class_start main // 1
class_start late_start
其中,class_start
是一个 COMMAND
,对应的函数是 do_class_start
。注释 1
处启动那些 classname
为 main
的 Service
,Zygote
的 classname
就是 main
,因此,class_start main
是用来启动 Zygote
的,do_class_start
函数在 builtins.cpp
中定义,如下所示:
// /system/core/init/builtins.cpp
static Result<Success> do_class_start(const BuiltinArguments& args) {
// Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
return Success();
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
for (const auto& service : ServiceList::GetInstance()) { // 1
if (service->classnames().count(args[1])) {
if (auto result = service->StartIfNotDisabled(); !result) {
LOG(ERROR) << "Could not start service '" << service->name()
<< "' as part of class '" << args[1] << "': " << result.error();
}
}
}
return Success();
}
在注释 1
处会遍历 Service
链表,找到 classname
为 main
的 Zygote
,并且执行 StartIfNotDisabled
函数,如下所示:
// /system/core/init/service.cpp
Result<Success> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) { // 1
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return Success();
}
在注释 1
处,如果 Service
没有找到其对应的 .rc
文件中设置的 disable
选项,则会调用 Start()
函数启动该 Service
,Zygote
对应的 init.zygote64.rc
中并没有设置 disabled
选项,因此接着查看 Start()
函数:
// /system/core/init/service.cpp
Result<Success> Service::Start() {
...
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
// 如果Service已经运行,则不启动
if (flags_ & SVC_RUNNING) {
if ((flags_ & SVC_ONESHOT) && disabled) {
flags_ |= SVC_RESTART;
}
// It is not an error to try to start a service that is already running.
return Success();
}
...
// 判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
flags_ |= SVC_DISABLED;
return ErrnoError() << "Cannot find '" << args_[0] << "'";
}
...
// 如果子进程没有启动,则调用fork函数创建子进程
pid_t pid = -1; // 1
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork();
}
// 当前代码逻辑在子进程中运行
if (pid == 0) { // 2
umask(077);
...
if (!ExpandArgsAndExecv(args_, sigstop_)) {
PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
}
_exit(127);
}
}
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
std::vector<std::string> expanded_args;
std::vector<char*> c_strings;
expanded_args.resize(args.size());
c_strings.push_back(const_cast<char*>(args[0].data()));
for (std::size_t i = 1; i < args.size(); ++i) {
if (!expand_props(args[i], &expanded_args[i])) {
LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
}
c_strings.push_back(expanded_args[i].data());
}
c_strings.push_back(nullptr);
if (sigstop) {
kill(getpid(), SIGSTOP);
}
// 调用execv函数,启动service子进程
return execv(c_strings[0], c_strings.data()) == 0; // 3
}
首先判断 Service
是否已经运行,如果运行则不再启动,如果程序走到注释 1
处,说明子进程还没有被启动,就调用 fork()
函数创建子进程,并返回 pid
值,注释 2
处,如果 pid == 0
,则说明当前代码的逻辑在子线程中运行。在注释 3
处调用 execv
函数,Service
子进程就会被启动,并且进入该 Service
的 main
函数中。
如果该 Service
是 Zygote
,Zygote
的执行路径为 system/bin/app_process64
,对应的文件为 app_main.cpp
,这样就会进入 app_main.cpp
的 main
函数,也就是在 Zygote
的 main
函数中,代码如下:
// frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote); // 1
} 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.");
}
}
注释 1
处的代码是调用 runtime.start
函数启动 Zygote
,至此,Zygote
就启动了。