本系列文章简要介绍 Android 系统启动的流程,不会拘泥于源码细节,旨在让读者了解大概的流程。另外,需要提醒大家注意的一点是,阅读本章需要有一定的 C/C++基础,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程,本系列文章都将基于Android8.1源码来分析Android系统的启动流程。
init进程是Android系统中用户空间的第一个进程,进程号为1,是Android系统启动流程中的一个关键的步骤,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建Zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些源文件位于源码目录system/core/init中。
为了讲解 init 进程,首先要了解 Android 系统启动流程的前几步,以引入 init 进程。
在Linux内核加载完成之后,它首先在系统文件中寻找init.rc文件,并启动init进程,然后查看init进程的入口函数main,代码如下所示:
system/core/init/init.cpp
int main(int argc, char** argv) {
// ... 1
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
// ... 2
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
// ... 3
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
// 添加环境变量
add_environment("PATH", _PATH_DEFPATH);
// 获取本次启动是否是系统启动的第一阶段,如果是第一阶段,
// 则进入下面的if语句中,创建并挂载相关的文件系统。
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
//创建文件并挂载
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
// Clear the umask.
umask(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.
// 创建和挂载启动所需的文件目录
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));
// Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
// 初始化Kernel的Log,这样就可以从外界获取Kernel的日志
InitKernelLogging(argv);
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic();
}
SetInitAvbVersionInRecovery(); // ... 4
// Set up SELinux, loading the SELinux policy.
// 启动SELinux。
selinux_initialize(true);
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (selinux_android_restorecon("/init", 0) == -1) {
PLOG(ERROR) << "restorecon failed";
security_failure();
}
setenv("INIT_SECOND_STAGE", "true", 1);
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
char* path = argv[0];
char* args[] = { path, nullptr };
execv(path, args);
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(ERROR) << "execv(\"" << path << "\") failed";
security_failure();
}
// At this point we're in the second stage of init.
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
// Set up a session keyring that all processes will have access to. It
// will hold things like FBE encryption keys. No process should override
// its session keyring.
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
// 初始化属性服务
property_init(); // ....... 5
// 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();
// Make the time that init started available for bootstat to log.
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
// Set libavb version for Framework-only OTA match in Treble build.
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Clean up our environment.
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
selinux_initialize(false);
selinux_restore_context();
// 创建epoll句柄
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(ERROR) << "epoll_create1 failed";
exit(1);
}
// 用于设置子进程信号处理函数,如果子进程(Zygote进程)异常退出,init进程会调用该函数中
// 设定的信号处理函数来进行处理
signal_handler_init(); // ...... 6
// 导入默认的环境变量
property_load_boot_defaults();
export_oem_lock_status();
// 启动属性服务
start_property_service(); // ...... 7
set_usb_controller();
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service", std::make_unique(&sm));
parser.AddSectionParser("on", std::make_unique(&am));
parser.AddSectionParser("import", std::make_unique(&parser));
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
// 解析init.rc配置文件
parser.ParseConfig("/init.rc"); // ...... 8
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
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(set_kptr_restrict_action, "set_kptr_restrict");
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_done
am.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 = GetProperty("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) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
// 遍历执行每个action中携带的command对应的执行函数
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
if (!shutting_down)
// 重启死去的进程
restart_processes(); // ...... 9
// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {
epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
init的main函数中我们只需关注一下几点就可以了。
注释1处判断当前进程是不是ueventd。init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd。ueventd主要是负责设备节点的创建、权限设定等一系列工作。服务通过使用uevent,监控驱动发送的消息,做进一步处理。
ueventd通过两种方式创建设备节点文件。
1.“冷插拔”(Cold Plug),即以预先定义的设备信息为基础,当ueventd启动后,统一创建设备节点文件。这一类设备节点文件也被称为静态节点文件。
2.“热插拔”(Hot Plug),即在系统运行中,当有设备插入USB端口时,ueventd就会接收到这一事件,为插入的设备动态创建设备节点文件。这一类设备节点文件也被称为动态节点文件。
注释2处判断当前进程是不是watchdogd。Android系统在长时间的运行下会面临各种软硬件的问题,为了解决这个问题,Android开发了WatchDog类作为软件看门狗来监控SystemServer中的线程,一旦发现问题,WatchDog会杀死system_server进程,system_server的父进程zygote接收到system_server的死亡信号后,会杀死自己。zygote进程死亡的信号传递到init进程后,init进程会杀死zygote进程所有的子进程并重启zygote 。
注释3处判断是否紧急重启,如果是紧急重启,就安装对应的消息处理器。
在系统启动的第一阶段中会清理umask,然后创建和挂载启动所需要的文件目录,其中挂载了 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文件系统是Linux2.6内核引入的,它把连接在系统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。
selinuxfs:用于支持SELinux的文件系统,SELinux提供了一套规则来编写安全策略文件,这套规则被称之为 SELinux Policy 语言
在注释4处初始化安全框架AVB(Android Verified Boot),AVB主要用于防止系统文件本身被篡改,还包含了防止系统回滚的功能,以免有人试图回滚系统并利用以前的漏洞。
在注释5处调用 property_init 函数来对属性进行初始化,并在注释7处调用 start_property_service 函数启动属性服务。
在注释6处调用 signal_handler_init 函数用于设置子进程信号处理函数,它被定义在 system/core/init/ signal_handler.cpp 中,主要用于防止 init 进程fork出的子进程成为僵尸进程(僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程), 为了防止僵尸进程的出现,系统会在子进程暂停和终止的时候发出 SIGCHLD 号,而 signal_handler_init 函数就是用来接收 SIGCHLD 信号的(其内部只处理进程终止的 SIGCHLD 信号)。
假设 init 进程fork出的子进程 zygote 终止了, signal_handler_init 函数内部会调用 handle_signal 函数,最终会找到 zygote 进程并移除所有的 zygote 进程的信息,再重启 zygote 服务的启动脚本(比如 init.zygote64.rc )中带有 onrestart 选项的服务, 关于 init.zygote64 .re 后面会讲到,至于 zygote 进程本身会在注释9处被重启。这里只是拿 zygote 进程举个例子,其他 init 进程子进程的原理也是类似的。
注释8处用来解析 init.rc 文件,解析 init.rc 的文件为 system/core/init/init_parse.cpp 文件, 接下来我们查看 init.rc 里做了什么。
init.rc是一个非常重要的配置文件,它是由Android初始化语言(Android Init Language)编写的脚本,它主要包含五种类型语句:Action(Action中包含了一系列的Command)、Commands(init语言中的命令)、Services(由init进程启动的服务)、Options(对服务进行配置的选项)和Import(引入其他配置文件)。init.rc的配置代码如下所示。
system/core/rootdir/init. rc
on init
sysclktz 0
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /default.prop /dev/urandom
...
on boot
# basic network init
ifup lo
hostname localhost
domainname localdomain
...
这里只截取了一部分代码,其中#是注释符号。on init和on boot是Action类型语句,它的格式为:
on [&& ]*
...
Action都是以on开始,随后会定义触发器(trigger),接着便是为其定义命令(Commmand),当触发器条件满足时,command会被执行。Action 型语句采用ActionParser 来进行解析。
为了分析如何创建zygote进程,首先先了解Services类型语句,Service是一些在系统初始化时就启动或者退出时需要重启的程序。其格式如下:
service [ ]*
首先需要为服务定义名字,并指定程序路径,然后便是通过option来修饰服务(决定了服务在什么时候运行以及怎样运行), Service 型语句采用 ServiceParser 来进行解析
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc
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
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
其中service用于通知init进程创建名zygote的进程,这个zygote进程执行程序的路径为/system/bin/app_process64,后面的则是要传给app_process64的参数。class main指的是zygote的class name为main,后文会用到它。
bool ServiceParser::ParseSection(std::vector&& args, const std::string& filename,
int line, std::string* err) {
if (args.size() < 3) {
// 判断Services 是否有name 和 program(可执行程序)
*err = "services must have a name and a program";
return false;
}
const std::string& name = args[1];
if (!IsValidName(name)) {
// 检查Service的name是否有效
*err = StringPrintf("invalid service name '%s'", name.c_str());
return false;
}
// 在service_manager中查找有没有名字为name的Service
Service* old_service = service_manager_->FindServiceByName(name);
if (old_service) {
// 判断是否存在重名的Service
*err = "ignored duplicate definition of service '" + name + "'";
return false;
}
std::vector str_args(args.begin() + 2, args.end());
service_ = std::make_unique(name, str_args); // ... 1
return true;
}
bool ServiceParser::ParseLineSection(std::vector&& args, int line, std::string* err) {
return service_ ? service_->ParseLine(std::move(args), err) : false;
}
void ServiceParser::EndSection() {
if (service_) {
service_manager_->AddService(std::move(service_));
}
}
void ServiceManager::AddService(std::unique_ptr service) {
services_.emplace_back(std::move(service)); // ... 1
}
...
import /init.${ro.zygote}.rc
...
on nonencrypted
class_start main
class_start late_start
其中class_start是一个command,对应的函数为do_class_start。我们知道main指的就是Zygote,因此class_start main用来启动Zygote。do_class_start函数在builtins.cpp中定义,如下所示。
system/core/init/builtins.cpp
static int do_class_start(const std::vector& args) {
/* Starting a class does not start services
* which are explicitly disabled. They must
* be started individually.
*/
ServiceManager::GetInstance().
ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
return 0;
}
ForEachServicelnClass 函数会遍历 Service 链表,找到 class name 为main的Zygote ,并执行 StartlfNotDisabled 函数,如下所示:
system/core/init/service.cpp
bool Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) { // ... 1
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return true;
}
system/core/init/service.cpp
bool Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
// Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart
// on exit, unless they are ONESHOT.
// 判断Service是否正在运行,如果是,直接返回,避免重复启动。
if (flags_ & SVC_RUNNING) {
return false;
}
// 是否需要控制台环境
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console) {
if (console_.empty()) {
console_ = default_console;
}
// Make sure that open call succeeds to ensure a console driver is
// properly registered for the device node
int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
if (console_fd < 0) {
PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
flags_ |= SVC_DISABLED;
return false;
}
close(console_fd);
}
//判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
flags_ |= SVC_DISABLED;
return false;
}
std::string scon;
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
scon = ComputeContextFromExecutable(name_, args_[0]);
if (scon == "") {
return false;
}
}
LOG(INFO) << "starting service '" << name_ << "'...";
//1 如果子进程没有启动,则调用 fork 函数创建子进程
pid_t pid = -1;
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork(); //重点,通过fock来创建子进程
}
if (pid == 0) { //2 运行在子进程中
umask(077);
if (namespace_flags_ & CLONE_NEWPID) {
// This will fork again to run an init process inside the PID
// namespace.
SetUpPidNamespace(name_);
}
for (const auto& ei : envvars_) {
add_environment(ei.name.c_str(), ei.value.c_str());
}
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
// See if there were "writepid" instructions to write to files under /dev/cpuset/.
auto cpuset_predicate = [](const std::string& path) {
return StartsWith(path, "/dev/cpuset/");
};
auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
if (iter == writepid_files_.end()) {
// There were no "writepid" instructions for cpusets, check if the system default
// cpuset is specified to be used for the process.
std::string default_cpuset = GetProperty("ro.cpuset.default", "");
if (!default_cpuset.empty()) {
// Make sure the cpuset name starts and ends with '/'.
// A single '/' means the 'root' cpuset.
if (default_cpuset.front() != '/') {
default_cpuset.insert(0, 1, '/');
}
if (default_cpuset.back() != '/') {
default_cpuset.push_back('/');
}
writepid_files_.push_back(
StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
}
}
std::string pid_str = std::to_string(getpid());
for (const auto& file : writepid_files_) {
if (!WriteStringToFile(pid_str, file)) {
PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
}
}
if (ioprio_class_ != IoSchedClass_NONE) {
if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
PLOG(ERROR) << "failed to set pid " << getpid()
<< " ioprio=" << ioprio_class_ << "," << ioprio_pri_;
}
}
if (needs_console) {
setsid();
OpenConsole();
} else {
ZapStdio();
}
// As requested, set our gid, supplemental gids, uid, context, and
// priority. Aborts on failure.
SetProcessAttributes();
if (!ExpandArgsAndExecve(args_)) { //3 重点,开启子进程执行。
PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
}
_exit(127);
}
if (pid < 0) {
PLOG(ERROR) << "failed to fork for '" << name_ << "'";
pid_ = 0;
return false;
}
if (oom_score_adjust_ != -1000) {
std::string oom_str = std::to_string(oom_score_adjust_);
std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
if (!WriteStringToFile(oom_str, oom_file)) {
PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
}
}
time_started_ = boot_clock::now();
pid_ = pid;
flags_ |= SVC_RUNNING;
process_cgroup_empty_ = false;
errno = -createProcessGroup(uid_, pid_);
if (errno != 0) {
PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
<< name_ << "'";
} else {
if (swappiness_ != -1) {
if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
PLOG(ERROR) << "setProcessGroupSwappiness failed";
}
}
if (soft_limit_in_bytes_ != -1) {
if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
PLOG(ERROR) << "setProcessGroupSoftLimit failed";
}
}
if (limit_in_bytes_ != -1) {
if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
PLOG(ERROR) << "setProcessGroupLimit failed";
}
}
}
if ((flags_ & SVC_EXEC) != 0) {
LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
<< supp_gids_.size() << " context "
<< (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
}
NotifyStateChange("running");
return true;
}
在Start函数中,首先判断Service是否已经运行,如果已经运行了就不会再启动,直接返回false。当程序执行到注释1处,说明子进程还没有被启动,调用fork函数来创建子进程,并返回pid值看,在注释2处如果pid=0,则说明当前代码逻辑执行在子进程中,注释3处在子进程中调用ExpandArgsAndExecve函数,接下来看看ExpandArgsAndExecve函数做了什么事情,代码如下:
static bool ExpandArgsAndExecve(const std::vector& args) {
std::vector expanded_args;
std::vector c_strings;
expanded_args.resize(args.size());
c_strings.push_back(const_cast(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);
return execve(c_strings[0], c_strings.data(), (char**)ENV) == 0; // ... 1
}
在ExpandArgsAndExecve函数中,调用注释1处的execve函数,Service子进程就会被启动,并进入该Service的main函数,如果该Service是Zygote,上文中有提到过Zygote的执行路径为system/bin/app_process64,这样就会进入frameworks/base/cmds/app_process/App_main. cpp的main函数,也就是Zygote的main函数,代码如下:
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.");
}
}
// 初始化属性服务
property_init();
// 启动属性服务
start_property_service();
这两行代码用来初始化属性服务配置并启动属性服务。 首先我们来学习属性服务配置的初始化和启动。
void property_init() {
if (__system_property_area_init()) {
LOG(ERROR) << "Failed to initialize property area";
exit(1);
}
}
__system_property_area_init函数用来初始化属性内存区域。接下来查看start_property_service函数的具体代码:
void start_property_service() {
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, sehandle); // ... 1
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
}
listen(property_set_fd, 8); // ... 2
register_epoll_handler(property_set_fd, handle_property_set_fd); // ... 3
}
在注释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 进程将调用 handle_property_set_fd 函数进行处理。
在Linux 新的内核中, epoll 用来替换 select, epoll是 Linux 内核为处理大批量文件描述符而做了改进的 poll ,是Linux下多路复用 I/0 接口 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;
handle_property_set(socket, prop_value, prop_value, true); // ... 1
break;
}
...
}
}
在注释1处说明handle_property_set_fd函数通过调用handle_property_set函数来处理客户端请求,代码如下:
system/core/init/Property_service.cpp
static void handle_property_set(SocketConnection& socket,
const std::string& name,
const std::string& value,
bool legacy_protocol) {
...
// 以"ctl. "开头,表示是控制属性
if (android::base::StartsWith(name, "ctl.")) { // ... 1
// 检查客户端权限
if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
// 设置控制属性
handle_control_message(name.c_str() + 4, value.c_str()); // ... 2
if (!legacy_protocol) {
socket.SendUint32(PROP_SUCCESS);
}
} else {
LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
<< " service ctl [" << value << "]"
<< " uid:" << cr.uid
<< " gid:" << cr.gid
<< " pid:" << cr.pid;
if (!legacy_protocol) {
socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
}
}
} else {
// 非控制属性,普通属性,
// 检查客户端权限
if (check_mac_perms(name, source_ctx, &cr)) {
uint32_t result = property_set(name, value); // ... 3
if (!legacy_protocol) {
socket.SendUint32(result);
}
} else {
LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
if (!legacy_protocol) {
socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
}
}
}
freecon(source_ctx);
}
由以上代码可以知道,系统属性分为两种类型:一种是普通属性,还有一种是控制属性。控制属性用来执行一些命令 ,比如开机的动画就使用了这种属性。 注释1处的属性名称以“ctl.”开头,就说明是控制属性,然后调用 handle_control_message 函数来修改控制属性。如果是普通属性,调用注释3处的 property_set 函数来对普通属性进行修改,如下所示:
system/core/init/Property_service.cpp
uint32_t property_set(const std::string& name, const std::string& value) {
if (name == "selinux.restorecon_recursive") {
return PropertySetAsync(name, value, RestoreconRecursiveAsync);
}
return PropertySetImpl(name, value); // ... 1
}
在property_set方法中,首先判断属性的名字是否为“selinux.restorecon_recursive”,若果是则使用PropertySetAsync函数完成设置,如果不是则使用PropertySetImpl函数完成设置,这里我们分析PropertySetImpl函数完成属性设置,代码如下:
system/core/init/Property_service.cpp
static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
size_t valuelen = value.size();
// 判断属性的名字是否合法
if (!is_legal_property_name(name)) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
return PROP_ERROR_INVALID_NAME;
}
if (valuelen >= PROP_VALUE_MAX) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "value too long";
return PROP_ERROR_INVALID_VALUE;
}
// 从属性存储空间查找该属性
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
// 判断属性是否存在
if (pi != nullptr) {
// ro.* properties are actually "write-once".
// 如果属性名称以"ro."开头,表示只读属性,不能修改,直接返回。
if (android::base::StartsWith(name, "ro.")) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "property 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) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "__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 && android::base::StartsWith(name, "persist.")) {
write_persistent_property(name.c_str(), value.c_str());
}
property_changed(name, value);
return PROP_SUCCESS;
}
相关文章
Android系统启动流程(2) —— 解析Zygote进程启动过程
Android系统启动流程(3) —— 解析SystemServer进程启动过程
Android系统启动流程(4) —— 解析Launcher启动过程