Android
The startup of the device must go through three phases: Boot Loader
, Linux Kernel
and Android
system services. Strictly speaking, Android
the system is actually a series of "service processes" running on Linux
the kernel, and the "ancestor" of these service processes is init
the process.
Boot Loader
It is a small program before the operating system kernel runs. Through this small program, the hardware device can be initialized, and the mapping map of the memory space can be established, so as to bring the software and hardware environment of the system to a suitable state, so as to prepare the correct environment for the final call of the operating system kernel.
init
The process is Android
the first process in the user space in the system, PID
(process number) is 1
, it is Android
a key step in the system startup process, as the first process, it is given a lot of extremely important work responsibilities, it is init.rc
constructed by parsing files The initial running state of the system, that is, Android
most of the system services init.rc
are described in the script file and started according to certain conditions.
init
Process startup has done a lot of work, in general, it mainly does the following three things:
- Create (
mkdir
) and mount (mount
) the file directories required for startup; - Initialize and start property service(
property service
); - Parse
init.rc
the configuration file and startZygote
the process;
init
A process is composed of multiple source files, which are located in the source directory system/core/init
.
After the kernel is loaded, it will first look for the file ( ) Linux
in the system files , and start the process. init.rc
/system/core/rootdir/init.rc
init
The entry function of the process has been adjusted. It is no longer the function of the previous , but has been replaced with the function of . Android 10
The purpose of this is to separate the work of each stage and make the code logic more concise. **The code is as follows Shown: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
}
The first stage: create and mount the file directory required for startup
1
The code at the comment is init
the first stage of the process, which is implemented first_state_init.cpp
in the file ( /system/core/init/first_stage_init.cpp
), and the code looks like this:
// /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);
...
}
As can be seen from the above code, init
the first stage of the process is mainly used to create and mount the file directories required for startup , in which tmpfs
, devpts
, proc
, sysfs
and selinuxfs
are mounted 5
file systems, which are all system runtime directories, that is to say , will only exist when the system is running, and will disappear when the system is stopped.
Four types of file systems:
tmpfs
: A virtual memory file system that stores all files in the virtual memory. Iftmpfs
the file system is unmounted, all the contents under it will no longer exist.tmpfs
Both can be usedRAM
, or swap partition can be used, and the size will be changed according to actual needs.tmpfs
The speed of is amazing, after all, it resides inRAM
, even with the swap partition, the performance is still very good. Sincetmpfs
the resides inRAM
, its contents are not persistent. After the power is turned off,tmpfs
the content of the content disappears, which is alsotmpfs
the root cause of being called ;devpts
: Provides a standard interface for the pseudo-terminal, and its standard mount point is/dev/pts
. As long aspty
the main compound device of is opened, a new device file/dev/ptmx
will be dynamically created/dev/pts
under ;pty
proc
: A very important virtual file system, which can be regarded as the interface of the internal data structure of the kernel, through which we can obtain system information, and can also modify specific kernel parameters at runtime;sysfs
:proc
Similar to the file system, it is also a virtual file system that does not occupy any disk space. It is usually mounted/sys
under the directory.sysfs
The file system isLinux 2.6
introduced by the kernel, which organizes the devices and buses connected to the system into a hierarchical file so that they can be accessed in user space;
Phase 2: Initialize and start the property service
// 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
Call the function at the comment property_init()
to initialize the property, and 3
call StartPropertyService
the function at the comment to start the property service.
2
Calling the function at the comment InstallSignalFdHandler
is used to set the subprocess signal processing function, mainly to prevent init
the subprocess of the process from becoming a zombie process. In order to prevent the emergence of zombie processes, the system will send a signal when the child process is suspended or terminated SIGCHLD
, and InstallSignalFdHandler
the function is used to receive SIGCHLD
the signal (it only processes SIGCHLD
the signal of process termination internally).
zombie process
Zombie process and harm: In UNIX/Linux
, the parent process uses fork
the method to create a child process. After the child process terminates, if the parent process cannot be notified, although the child process has already exited, a certain amount of time is reserved for it in the system process table. Information (such as process number, exit status, running time, etc.), this process is called a zombie process. The system process table is a limited resource. If the system process table is exhausted by zombie processes, the system may not be able to create new processes.
attribute service
Window
There is a registry manager on the platform, and the content of the registry takes the form of key-value pairs to record some usage information of users and software. Even if the system or software is restarted, it can still perform corresponding initialization work according to the previous records in the registry. Android
A similar mechanism is also provided, called property services.
init
When the process starts, the attribute service will be started, and memory will be allocated for it to store these attributes. If you need to read these attributes directly, the code related to the attribute service in the function of will have the following two lines init.cpp
:main
// /system/core/init/init.cpp
property_init();
StartPropertyService(&epoll);
These two lines of code are used to initialize the property service configuration and start the property service.
1 Property service initialization and startup
property_init
The specific implementation of the function is as follows:
// /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
The function at comment __system_property_area_init()
is used to initialize the attribute memory area. Next look at StartPropertyService
the code of the function:
// /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
Create non-blocking at comment Socket
. 2
Call listen
the function at the comment property_set_fd
to listen to , so that the created Socket
becomes server
, that is, the attribute service; listen
the second parameter of the function is set to 8
, which means that the attribute service can 8
provide services for at most users who try to set attributes at the same time. 3
The code at the comment property_set_fd
puts in epoll
to epoll
monitor property_set_fd
: property_set_fd
when data arrives in , init
the process will call RegisterHandler
for processing.
In Linux
the new kernel, it is epoll
used to replace the kernel select
, which has been improved to handle a large number of file descriptors . It is an enhanced version of the multiplexing interface , which can significantly improve the performance of programs with only a small number of active connections in a large number of concurrent connections. The system utilization under the condition of . The internal data type used to save time is a red-black tree, which has a fast search speed. The array used to store information has a very slow search speed. Only when waiting for a small number of file descriptors, the efficiency of and will be similar.epoll
Linux
poll
Linux
I/O
select/poll
CPU
epoll
select
epoll
select
2 The service processes client requests
From the above, we can know that when the attribute service receives the request from the client, it will call handle_property_set_fd
the function to process it:
// /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;
}
...
}
The function is further encapsulated 1
at the comment , as shown below: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
}
There are two types of system attributes, one is common attributes, and the other is control attributes, which are used to execute some commands, such as the startup animation. Therefore, it HandlePropertySet
is divided into two processing points, one part deals with control attributes, and the other part is used to deal with common attributes. Only common attributes are analyzed here. If 1
the attribute name at the comment ctl.
starts with , it means it is a control attribute. If the client permission is satisfied, HandleControlMessage
the function will be called to modify the control attribute. If it is an ordinary attribute, under the condition of the client authority Mazu, the comment at the comment will be called 3
to PropertySet
modify the ordinary attribute, as shown below:
// /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
The function mainly modifies common attributes. Firstly, it is necessary to judge whether the attribute is legal. If it is legal, 1
search the attribute from the attribute storage space at the annotation. If the attribute exists, update the attribute value; otherwise, add the attribute. Also, ro.
persist.
properties whose names begin with are handled accordingly.
The third stage: parsing init.rc
the configuration file
// /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
is a very important configuration file, it is a script written by Android
the initialization language ( ), this language mainly contains three types of statements: , , , and .Android Init Language
5
Action(行为)
Command(命令)
Service(服务)
Option(选项)
Import(引入)
init.rc
The file is roughly divided into two parts, one part is the "action list" ( ) on
starting with the keyword , and the other part is the "service list" ( ) starting with the keyword .action list
service
service list
The action list is used to create the required directory and specify permissions for some specific files, and the service list is used to record init
some sub-processes that the process needs to start. As follows:
// /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
and on boot
is Action
a statement of type, its format is as follows:
on <triggger> [&& <trigger>]* // 设置触发器
<command>
<command> // 动作触发之后要执行的命令
For how to create Zygoet
, mainly look at Service
the type statement , its format is as follows:
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
Service
Type statement format:
service <name> <pathname> [<argument>]* // <service的名字><执行程序路径><传递参数>
<option> // option 是 service 的修饰词,影响什么时候、如何启动 service
<option>
...
It should be noted that the file is split in , and each service corresponds to a Android 8.0
file . Startup scripts are defined in , take for example :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
Analyze the meaning of the above code according to Service
the format of the type statement. Service
It is used to notify init
the process to create Zygote
a process named , the execution path of this process is /system/bin/app_process64
, and the following code is app_process64
the parameter to be passed to . class main
Refers to Zygote
the classname
for main
.
Service
parse type statement
init.rc
Both Action
the type statement and Service
the type statement in have corresponding classes for parsing, Action
the type statement uses ActionParser
for parsing, and Service
the type statement uses ServiceParser
for parsing. Because the main analysis is here Zygote
, it is only introduced ServiceParser
.
ServiceParser
The implementation code is system/core/init/service.cpp
in . Next, check ServiceParser
how to parse the above-mentioned Service
type of statement, two functions will be used: one is that ParseSection
it will parse the file Service
of .rc
the , for example, as mentioned above init.zygote64.rc
, ParseSection
the function is mainly used to build Service
the shelf of the; the other is ParseLineSection
, resolves subkeys forever. The code looks like this:
// 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();
}
At the comment 1
, an object is constructed according to the parameters Service
. The function is called after parsing the data 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
The function calls back ServiceManager.AddService
the function, the following is AddService
the function:
// system/core/init/service.cpp
void ServiceList::AddService(std::unique_ptr<Service> service) {
services_.emplace_back(std::move(service)); // 1
}
1
The code at the comment Service
adds the object to Service
the linked list. Service
Generally speaking, the parsing process is to create Service
the object according to the parameters, then fill Service
the object according to the content of the option field, and finally Service
add the object to the linked list vector
of type Service
.
Phase Four: Initiating Zygote
the Process
After understanding Service
the parsing process, the next step is init
how to start the process Service
. Here we mainly explain how to start Zygote
the process Service
. In Zygote
the startup script of , you can know the behavior Zygote
of . In has the following configuration code:classname
main
init.rc
// /system/core/rootdir/init.rc
on nonencrypted
class_start main // 1
class_start late_start
Among them, class_start
is one COMMAND
, and the corresponding function is do_class_start
. The comment 1
that starts those for classname
is , therefore , is used to start the function defined in as follows: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();
}
At the comment 1
, it will traverse Service
the linked list, find classname
the one that main
is Zygote
, and execute StartIfNotDisabled
the function, as shown below:
// /system/core/init/service.cpp
Result<Success> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
// 1
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return Success();
}
At the comment 1
, if the option set in Service
the corresponding file is not found , the function will be called to start it , and the option is not set in the corresponding , so then check the function:.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
}
First judge Service
whether has been running, if it is running, it will not start again, if the program goes to the comment 1
, it means that the sub-process has not been started, then call fork()
the function to create a sub-process, and return pid
the value, 2
if in the comment pid == 0
, it indicates the logic of the current code run in sub-threads. 3
Call the function at the comment execv
, Service
the child process will be started and enter the function Service
of the main
.
If the Service
is Zygote
, Zygote
the execution path is system/bin/app_process64
, and the corresponding file is app_main.cpp
, so it will enter the function app_main.cpp
of main
, that is, in the function Zygote
of main
, the code is as follows:
// 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
The code at the comment is to call runtime.start
the function to start Zygote
, so far, Zygote
it has started.
reference
init.rc Detailed
Analysis of SystemServer Service and ServiceManager Service
[Android 10 System Startup] Series – init (Tianzi No. 1 process)