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);
这两行代码用来初始化属性服务配置并启动属性服务。
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
的效率才会差不多。
从上面可以知道,属性服务接收到客户端的请求时,会调用 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 [&& ]* // 设置触发器
// 动作触发之后要执行的命令
对于如何创建 Zygoet
,主要查看 Service
类型语句,它的格式如下所示:
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
Service
类型的语句格式:
service []* // <执行程序路径><传递参数>
需要注意的是,在 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
就启动了。
init.rc详解
SystemServer服务和ServiceManager服务分析
【 Android 10 系统启动 】系列 – init(天字一号进程)