河北建设信息网seo网站优化推广
前言
先来简单看看Android系统启动的流程:
- 启动电源及系统启动
当电源按下时引导芯片代码从预定义的地方(固化在ROM)开始执行。加载引导程序Bootloader到RAM中,然后执行。 - 引导程序BootLoader
引导程序BootLoader是在Android操作系统开始运行前的一个小程序,它的主要作用是把系统OS拉起来并运行。 - Linux内核启动
当内核启动时,设置缓存、加载驱动等。在内核完成系统设置后,并启动init进程。 - init进程启动
作为用户空间的第一个进程(PID=1),init进程会解析init.rc脚本,启动关键底层服务ueventd(管理设备节点创建)、logd(日志系统)、servicemanager(BinderIPC和核心服务管理器),并设置环境变量和系统属性。 - Zygote进程孵化
由init进程根据init.rc启动,是Android应用进程的母体,加载核心Java类库和资源,预初始化常用类(减少应用启动时间),启动Socket监听,等待孵化新应用的请求。 - System Server启动
System Server初始化:由Zygote孵化出的第一个Java进程,AMS、PMS、WMS等核心系统服务在此启动,并将所有服务通过Binder IPC注册到servicemanager - Launcher与应用启动
System Server启动完成后,AMS启动桌面应用(Launcher),用户可见系统界面,后续应用均由Zygote孵化,通过Binder与SystemServer交互。
本篇我们主要梳理一下和init进程相关的源码。
引导程序BootLoader
在CPU上电复位完成后,会从一个固定的地址加载一段程序,即BootLoader,不同的CPU可能这个地址不同。BootLoader是一段引导程序,其中最为常见的为U-boot,它一般会先检测用户是否按下某些特别按键,这些特别按键是uboot在编译时预先被约定好的,用于进入调试模式。如果用户没有按这些特别的按键,则uboot会从NAND Flash中装载Linux内核,装载的地址是在编译uboot时预先约定好的。
-
idle 进程
Linux内核启动后,便会创建第一个进程idle。idle进程是Linux中的第一个进程,pid为0,是唯一一个没有通过fork产生的进程,它的优先级非常低,用于CPU没有任务的时候进行空转。 -
init 进程
init进程由idle进程创建,是Linux系统的第一个用户进程,pid为1,是系统所有用户进程的直接或间接父进程,本篇重点讲的就是它。 -
kthreadd 进程
kthreadd进程同样由idle进程创建,pid为2,它始终运行在内核空间,负责所有内核线程的调度与管理。 -
kernel 启动init
kernel_init启动后,完成一些init的初始化操作,然后去系统根目录下依次找ramdisk_execute_command和execute_command设置的应用程序,如果这两个目录都找不到,就依次去根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动,只要这些应用程序有一个启动了,其他就不启动了。
kernel/msm-5.4/init/main.c
static noinline void __init kernel_init_freeable(void)
{if (!ramdisk_execute_command)ramdisk_execute_command = "/init";
}static int __ref kernel_init(void *unused)
{int ret;kernel_init_freeable();/* need to finish all async __init code before freeing the memory */async_synchronize_full();kprobe_free_init_mem();ftrace_free_init_mem();free_initmem();mark_readonly();pti_finalize();system_state = SYSTEM_RUNNING;numa_default_policy();rcu_end_inkernel_boot();#ifdef CONFIG_QGKI_MSM_BOOT_TIME_MARKERplace_marker("M - DRIVER Kernel Boot Done");
#endifif (ramdisk_execute_command) {ret = run_init_process(ramdisk_execute_command);if (!ret)return 0;pr_err("Failed to execute %s (error %d)\n",ramdisk_execute_command, ret);}if (execute_command) {ret = run_init_process(execute_command);if (!ret)return 0;panic("Requested init %s failed (error %d).",execute_command, ret);}if (!try_to_run_init_process("/sbin/init") ||!try_to_run_init_process("/etc/init") ||!try_to_run_init_process("/bin/init") ||!try_to_run_init_process("/bin/sh"))return 0;panic("No working init found. Try passing init= option to kernel. ""See Linux Documentation/admin-guide/init.rst for guidance.");
}
上面代码可以看到ramdisk_execute_command指向了系统根目录下init文件
系统根目录下的init是软连接,实际指向system/bin/init
init进程的入口
init进程是Linux系统中用户空间的第一个进程,进程号固定为1。Kernel启动后,在用户空间启动init进程,并调用init中的main()方法执行init进程的职责。init进程对应的init.cpp文件的入口函数如下所示:
system/core/init/main.cpp
using namespace android::init;int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)__asan_set_error_report_callback(AsanReportCallback);
#endif//将当前进程的优先级设置为最高(-20),确保init进程在系统启动初期能够快速执行,避免被其他进程抢占。setpriority(PRIO_PROCESS, 0, -20);//ueventd的主要工作,是通过两种方式创建设备节点文件:1.冷插拔(例如各板载设备);2.热插拔(如U盘)if (!strcmp(basename(argv[0]), "ueventd")) {return ueventd_main(argc, argv);}//如果main方法的参数大于1个,即至少2个及以上,则执行下面if块的代码//argc 大于1,根据上文提要,至少有2种情况:1./init subcontext 2./ueventd subcontextif (argc > 1) {if (!strcmp(argv[1], "subcontext")) {android::base::InitLogging(argv, &android::base::KernelLogger);const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();//调用SubcontextMain方法return SubcontextMain(argc, argv, &function_map);}if (!strcmp(argv[1], "selinux_setup")) {//调用SetupSelinux方法return SetupSelinux(argv);}if (!strcmp(argv[1], "second_stage")) {//调用SecondStageMain方法return SecondStageMain(argc, argv);}}//如果main方法不带任何参数,调用FirstStageMain方法return FirstStageMain(argc, argv);
}
FirstStageMain
main方法默认启动不是不带任何参数的,这个时候会触发FirstStageMain方法。
system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {//init崩溃时候重启系统if (REBOOT_BOOTLOADER_ON_PANIC) {InstallRebootSignalHandlers();}//注释1,使创建的目录或文件的默认权限为最高umask(0);//清理环境变量CHECKCALL(clearenv());//设置环境变量 #define _PATH_DEFPATH "/usr/bin:/bin"CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));//挂载目录为 /dev, 并开始在/dev下创建一系列文件,包括设备节点CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));CHECKCALL(mkdir("/dev/pts", 0755));CHECKCALL(mkdir("/dev/socket", 0755));CHECKCALL(mkdir("/dev/dm-user", 0755));//挂载devpts远程虚拟终端文件设备,文件夹里面一般是一些字符设备文件CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR//读取内核配置参数 , 可以到root 设备 /proc/cmdline 位置查看一下CHECKCALL(chmod("/proc/cmdline", 0440));std::string cmdline;android::base::ReadFileToString("/proc/cmdline", &cmdline);//读取Android 用户空间的配置参数。 可以到root 设备 /proc/bootconfig 位置查看一下chmod("/proc/bootconfig", 0440);std::string bootconfig;android::base::ReadFileToString("/proc/bootconfig", &bootconfig);//将当前进程添加到 AID_READPROC 进程组,从而拥有读取进程文件系统的权限gid_t groups[] = {AID_READPROC};CHECKCALL(setgroups(arraysize(groups), groups));//继续挂载所需的fs,创建所需的节点和目录CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));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));CHECKCALL(mkdir("/mnt/product", 0755));CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,"mode=0755,uid=0,gid=0"));CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,"mode=0755,uid=0,gid=0"))
#undef CHECKCALLSetStdioToDevNull(argv);//注释2,初始化内核日志系统,这样就可以从外界获取Kernel的日志了InitKernelLogging(argv);if (!errors.empty()) {for (const auto& [error_string, error_errno] : errors) {LOG(ERROR) << error_string << " " << strerror(error_errno);}LOG(FATAL) << "Init encountered errors starting first stage, aborting";}//注释3,打印关键日志init第一阶段启动完毕LOG(INFO) << "init first stage started!";auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};if (!old_root_dir) {PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";}struct stat old_root_info;if (stat("/", &old_root_info) != 0) {PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";old_root_dir.reset();}auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;boot_clock::time_point module_start_time = boot_clock::now();int module_count = 0;//加载kernel模块并调用ForceNormalBoot函数判断是否需要强制重启if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,module_count)) {if (want_console != FirstStageConsoleParam::DISABLED) {LOG(ERROR) << "Failed to load kernel modules, starting console";} else {LOG(FATAL) << "Failed to load kernel modules";}}if (module_count > 0) {auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - module_start_time);setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);LOG(INFO) << "Loaded " << module_count << " kernel modules took "<< module_elapse_time.count() << " ms";}bool created_devices = false;if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {if (!IsRecoveryMode()) {//创建驱动节点created_devices = DoCreateDevices();if (!created_devices){LOG(ERROR) << "Failed to create device nodes early";}}StartConsole(cmdline);}if (access(kBootImageRamdiskProp, F_OK) == 0) {std::string dest = GetRamdiskPropForSecondStage();std::string dir = android::base::Dirname(dest);std::error_code ec;if (!fs::create_directories(dir, ec) && !!ec) {LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();}if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": "<< ec.message();}LOG(INFO) << "Copied ramdisk prop to " << dest;}if (access("/force_debuggable", F_OK) == 0) {constexpr const char adb_debug_prop_src[] = "/adb_debug.prop";constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil";std::error_code ec; // to invoke the overloaded copy_file() that won't throw.if (access(adb_debug_prop_src, F_OK) == 0 &&!fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) {LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp<< ": " << ec.message();}if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&!fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) {LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "<< kDebugRamdiskSEPolicy << ": " << ec.message();}setenv("INIT_FORCE_DEBUGGABLE", "true", 1);}//判断是否需要强制重启if (ForceNormalBoot(cmdline, bootconfig)) {mkdir("/first_stage_ramdisk", 0755);// SwitchRoot() must be called with a mount point as the target, so we bind mount the// target directory to itself here.if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";}SwitchRoot("/first_stage_ramdisk");}if (!DoFirstStageMount(!created_devices)) {LOG(FATAL) << "Failed to mount required partitions early ...";}struct stat new_root_info;if (stat("/", &new_root_info) != 0) {PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";old_root_dir.reset();}if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);}SetInitAvbVersionInRecovery();setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),1);//注释4,通过execv函数带参执行init文件,进入SetupSelinuxconst char* path = "/system/bin/init";const char* args[] = {path, "selinux_setup", nullptr};auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);close(fd);execv(path, const_cast<char**>(args));//这里在末尾直接打log是因为,exec系列函数如果执行正常是不会返回的,所以只要执行到下面就代表exec执行出错了PLOG(FATAL) << "execv(\"" << path << "\") failed";return 1;
}
注释1处调用umask(0)方法主要是用来设置创建目录或文件时所应该赋予权限的掩码,Linux中,文件默认最大权限是666,目录最大权限是777,当创建目录时,假设掩码为022,那赋予它的权限为(777 & ~022)= 755,在执行init第一阶段时,先执行umask(0),使创建的目录或文件的默认权限为最高。
注释2处调用InitKernelLogging函数,初始化了一个简单的kernel日志系统。
注释3处打印了一段关键日志"init first stage started!",表示init第一阶段启动完毕。
注释4处通过execv函数带参执行init文件,进入SetupSelinux,用exec系列函数可以把当前进程替换为一个新进程,且新进程与原进程有相同的PID。
SetupSelinux
system/core/init/selinux.cpp
int SetupSelinux(char** argv) {SetStdioToDevNull(argv);InitKernelLogging(argv);if (REBOOT_BOOTLOADER_ON_PANIC) {InstallRebootSignalHandlers();}boot_clock::time_point start_time = boot_clock::now();MountMissingSystemPartitions();SelinuxSetupKernelLogging();LOG(INFO) << "Opening SELinux policy";// Read the policy before potentially killing snapuserd.std::string policy;ReadPolicy(&policy);auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();if (snapuserd_helper) {// Kill the old snapused to avoid audit messages. After this we cannot// read from /system (or other dynamic partitions) until we call// FinishTransition().snapuserd_helper->StartTransition();}LoadSelinuxPolicy(policy);if (snapuserd_helper) {// Before enforcing, finish the pending snapuserd transition.snapuserd_helper->FinishTransition();snapuserd_helper = nullptr;}SelinuxSetEnforcement();// We're in the kernel domain and want to transition to the init domain. File systems that// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,// but other file systems do. In particular, this is needed for ramdisks such as the// recovery image for A/B devices.if (selinux_android_restorecon("/system/bin/init", 0) == -1) {PLOG(FATAL) << "restorecon failed of /system/bin/init failed";}setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);const char* path = "/system/bin/init";const char* args[] = {path, "second_stage", nullptr};execv(path, const_cast<char**>(args));// execv() only returns if an error happened, in which case we// panic and never return from this function.PLOG(FATAL) << "execv(\"" << path << "\") failed";return 1;
}
上面代码启动Selinux安全机制,初始化selinux,加载SELinux规则,配置SELinux相关log输出,并启动第二阶段:SecondStageMain
需要注意一下在函数中调用MountMissingSystemPartitions函数挂载之前没有挂载的系统分区
SecondStageMain
system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {//init崩溃时候重启系统if (REBOOT_BOOTLOADER_ON_PANIC) {InstallRebootSignalHandlers();}boot_clock::time_point start_time = boot_clock::now();trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };// 初始化日志SetStdioToDevNull(argv);InitKernelLogging(argv);//注释1,关键日志打印LOG(INFO) << "init second stage started!";if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";}{struct sigaction action = {.sa_flags = SA_RESTART};action.sa_handler = [](int) {};sigaction(SIGPIPE, &action, nullptr);}//设置init进程和以后fork出来的进程的OOM等级,这里的值为-1000,保证进程不会因为OOM被杀死if (auto result =WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));!result.ok()) {LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST<< " to /proc/1/oom_score_adj: " << result.error();}keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);//设置一个标记,代表正在启动过程中close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");bool load_debug_prop = false;if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {load_debug_prop = "true"s == force_debuggable_env;}unsetenv("INIT_FORCE_DEBUGGABLE");// Umount the debug ramdisk so property service doesn't read .prop files from there, when it// is not meant to.if (!load_debug_prop) {UmountDebugRamdisk();}//注释2,初始化系统属性PropertyInit();UmountSecondStageRes();if (load_debug_prop) {UmountDebugRamdisk();}//挂载额外的文件系统MountExtraFilesystems();//设置SELinuxSelinuxSetupKernelLogging();SelabelInitialize();SelinuxRestoreContext();//使用epoll,注册信号处理函数,守护进程服务Epoll epoll;if (auto result = epoll.Open(); !result.ok()) {PLOG(FATAL) << result.error();}InstallSignalFdHandler(&epoll);InstallInitNotifier(&epoll);//启动系统属性服务StartPropertyService(&property_fd);RecordStageBoottimes(start_time);if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {SetProperty("ro.boot.avb_version", avb_version);}unsetenv("INIT_AVB_VERSION");fs_mgr_vendor_overlay_mount_all();export_oem_lock_status();MountHandler mount_handler(&epoll);SetUsbController();SetKernelVersion();//注释3,设置commands指令所对应的函数map,后面我们还会提到这个const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();Action::set_function_map(&function_map);if (!SetupMountNamespaces()) {PLOG(FATAL) << "SetupMountNamespaces failed";}InitializeSubcontext();//注释4,解析init.rc脚本ActionManager& am = ActionManager::GetInstance();ServiceList& sm = ServiceList::GetInstance();LoadBootScripts(am, sm);if (false) DumpState();auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";SetProperty(gsi::kGsiBootedProp, is_running);auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";SetProperty(gsi::kGsiInstalledProp, is_installed);//构建了一些Action,Trigger等事件对象加入事件队列中am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");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(SetMmapRndBitsAction, "SetMmapRndBits");Keychords keychords;am.QueueBuiltinAction([&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {for (const auto& svc : ServiceList::GetInstance()) {keychords.Register(svc->keycodes());}keychords.Start(&epoll, HandleKeychord);return {};},"KeychordInit");// Trigger all the boot actions to get us started.am.QueueEventTrigger("init");// 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");// Restore prio before main loopsetpriority(PRIO_PROCESS, 0, 0);while (true) {// By default, sleep until something happens.auto epoll_timeout = std::optional<std::chrono::milliseconds>{};auto shutdown_command = shutdown_state.CheckShutdown();if (shutdown_command) {LOG(INFO) << "Got shutdown_command '" << *shutdown_command<< "' Calling HandlePowerctlMessage()";HandlePowerctlMessage(*shutdown_command);shutdown_state.set_do_shutdown(false);}if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {am.ExecuteOneCommand();}if (!IsShuttingDown()) {auto next_process_action_time = HandleProcessActions();// If there's a process that needs restarting, wake up in time for that.if (next_process_action_time) {epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(*next_process_action_time - boot_clock::now());if (*epoll_timeout < 0ms) epoll_timeout = 0ms;}}//执行从init.rc脚本解析出来的每条指令if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {// If there's more work to do, wake up again immediately.if (am.HasMoreCommands()) epoll_timeout = 0ms;}auto pending_functions = epoll.Wait(epoll_timeout);if (!pending_functions.ok()) {LOG(ERROR) << pending_functions.error();} else if (!pending_functions->empty()) {ReapAnyOutstandingChildren();for (const auto& function : *pending_functions) {(*function)();}}if (!IsShuttingDown()) {HandleControlMessages();SetUsbController();}}return 0;
}
在注释1处有一个关键日志打印"init second stage started!"
在注释2处初始化系统属性,随后启动属性服务
在注释3处设置commands指令所对应的函数map,这个主要是为了.rc脚本的解析,后面我们还会提到这个
在注释4处开始解析init.rc脚本。
属性服务
在Android系统中,所有的进程共享系统设置值,为此提供一个名称为属性的保存空间。init进程调用property_init函数,在共享内存区域中创建并初始化属性域。而后通过执行进程所提供的API访问属性域中的设置值。但更改属性域时,要预先向init进程提交值变更申请,然后init进程处理该申请,并修改属性值(用propert_set()和property_get()来处理)
/system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {//注释2,初始化系统属性PropertyInit();...代码省略...//启动系统属性服务StartPropertyService(&property_fd);
}
/system/core/init/property_service.cpp
void PropertyInit() {selinux_callback cb;cb.func_audit = PropertyAuditCallback;selinux_set_callback(SELINUX_CB_AUDIT, cb);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";}// If arguments are passed both on the command line and in DT,// properties set in DT always have priority over the command-line ones.ProcessKernelDt();ProcessKernelCmdline();ProcessBootconfig();// Propagate the kernel variables to internal variables// used by init as well as the current required properties.ExportKernelBootProps();PropertyLoadBootDefaults();
}
void StartPropertyService(int* epoll_socket) {InitPropertySet("ro.property_service.version", "2");int sockets[2];if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {PLOG(FATAL) << "Failed to socketpair() between property_service and init";}*epoll_socket = from_init_socket = sockets[0];init_socket = sockets[1];StartSendingMessages();//创建用于接收属性变更请求的socket (/dev/socket/property_service)if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,false, 0666, 0, 0, {});result.ok()) {property_set_fd = *result;} else {LOG(FATAL) << "start_property_service socket creation failed: " << result.error();}listen(property_set_fd, 8);auto new_thread = std::thread{PropertyServiceThread};property_service_thread.swap(new_thread);
}
属性值的更改仅能在init进程中进行,即一个进程若想修改属性值,必须向init进程提交申请,为此init进程生成“/dev/socket/property_service”套接字,以接收其它进程提交的申请。
Android初始化脚本
rc文件,是用Android Init Language编写的特殊文件。用这种语法编写的文件,统一用".rc"后缀
它的语法说明可以在aosp源码system/core/init/README.md中找到,这里就简单说明一下语法规则。
- Actions
Actions是一系列命令的开始,一个Action会有一个触发器,用于确定Action何时执行。当一个与Action的触发器匹配的事件发生时,该动作被添加到待执行队列的尾部
格式如下:
on <trigger> [&& <trigger>]* <command> <command> <command>
-
Triggers(触发器)
触发器作用于Actions,可用于匹配某些类型的事件,并用于导致操作发生 -
Commands
Commands就是一个个命令的集合了
Action,Triggers, Commands共同组成了一个单元,举个例子:
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencryptedwait_for_prop odsign.verification.done 1# A/B update verifier that marks a successful boot.exec_start update_verifier_nonencryptedstart statsdstart netdstart zygotestart zygote_secondary
- Services
Services是对一些程序的定义,格式如下:
service <name> <pathname> [ <argument> ]*<option><option>...
其中: name:定义的服务名
pathname:这个程序的路径
argument:程序运行的参数
option:服务选项,后文将介绍
- Options
Options是对Services的修饰,它们影响着服务运行的方式和时间
Services, Options组成了一个单元,举个例子:
system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-serverclass mainpriority -20user rootgroup root readproc reserved_disksocket zygote stream 660 root systemsocket usap_pool_primary stream 660 root systemonrestart exec_background - system system -- /system/bin/vdc volume abort_fuseonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/taskscritical window=${zygote.critical_window.minute:-off} target=zygote-fatal
- Imports
导入其他的rc文件或目录解析,如果path是一个目录,目录中的每个文件都被解析为一个rc文件。它不是递归的,嵌套的目录将不会被解析。
import <path>
Imports的内容会放到最后解析
上文所述的Commands,Options等具体命令,可以网上搜索一下,或者自己看system/core/init/README.md
Commands的定义可以在system/core/init/builtins.cpp中找到
Options的定义可以在system/core/init/service_parser.cpp中找到
init.rc脚本
system/core/rootdir/init.rc
import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/hw/init.usb.configfs.rc
import /system/etc/init/hw/init.${ro.zygote}.rc
另外在发现目录中有init.zygote32.rc,init.zygote64.rc,init.zygote64_32.rc
来看下实际运行中的系统中,/system/etc/init/hw目录的具体内容。
init.rc 解析
上面简单介绍了rc 文件的语法,下面看一下rc 文件是在哪里进行解析的。
在SecondeStageMain函数中执行LoadBootScripts函数来解析rc文件
system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {//注释1,创建Parser解析器Parser parser = CreateParser(action_manager, service_list);std::string bootscript = GetProperty("ro.boot.init_rc", "");if (bootscript.empty()) {//注释2,调用ParseConfig方法解析.rc文件parser.ParseConfig("/system/etc/init/hw/init.rc");if (!parser.ParseConfig("/system/etc/init")) {late_import_paths.emplace_back("/system/etc/init");}parser.ParseConfig("/system_ext/etc/init");if (!parser.ParseConfig("/vendor/etc/init")) {late_import_paths.emplace_back("/vendor/etc/init");}if (!parser.ParseConfig("/odm/etc/init")) {late_import_paths.emplace_back("/odm/etc/init");}if (!parser.ParseConfig("/product/etc/init")) {late_import_paths.emplace_back("/product/etc/init");}} else {parser.ParseConfig(bootscript);}
}
以上代码主要是在不同的地方寻找rc文件并进行解析,/system/etc/init/hw/init.rc是主rc文件,剩下的目录,如果system分区尚未挂载的话,就把它们加入到late_import_paths中,等到后面mount_all时再加载,主rc文件在编译前的位置为system/core/rootdir/init.rc。
在注释1处,以ActionManager和ServiceList作为参数创建了一个Parser解析器,解析后的结果会存放在ActionManager和ServiceList中,这里两个传进来的参数都是单例模式。
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {Parser parser;parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt));parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));return parser;
}
先创建了一个Parser对象,然后往里面添加了ServiceParser、ActionParser以及ImportParser,这三个类都是继承自ServiceParser,这里的std::make_unique是new了一个对象,并用其原始指针构造出了一个智能指针。在注释2处调用ParseConfig方法进行数据解析。
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {Parser parser;parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt));parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));return parser;
}
判断是否是目录,如果是目录,就把目录中的所有文件加入容器中排序后依次解析,详细解析方式这里不做过多介绍。
执行rc文件中的任务
在SecondStageMain方法中,最后面可以看到有一个死循环,是用来等待事件处理。
/system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {...代码省略...while (true) {// By default, sleep until something happens.auto epoll_timeout = std::optional<std::chrono::milliseconds>{};auto shutdown_command = shutdown_state.CheckShutdown();if (shutdown_command) {LOG(INFO) << "Got shutdown_command '" << *shutdown_command<< "' Calling HandlePowerctlMessage()";HandlePowerctlMessage(*shutdown_command);shutdown_state.set_do_shutdown(false);}if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {//注释1,执行从init.rc脚本解析出来的指令am.ExecuteOneCommand();}if (!IsShuttingDown()) {auto next_process_action_time = HandleProcessActions();// If there's a process that needs restarting, wake up in time for that.if (next_process_action_time) {epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(*next_process_action_time - boot_clock::now());if (*epoll_timeout < 0ms) epoll_timeout = 0ms;}}if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {// If there's more work to do, wake up again immediately.if (am.HasMoreCommands()) epoll_timeout = 0ms;}auto pending_functions = epoll.Wait(epoll_timeout);if (!pending_functions.ok()) {LOG(ERROR) << pending_functions.error();} else if (!pending_functions->empty()) {ReapAnyOutstandingChildren();for (const auto& function : *pending_functions) {(*function)();}}if (!IsShuttingDown()) {HandleControlMessages();SetUsbController();}}return 0;
}
/system/core/init/action_manager.cpp
void ActionManager::ExecuteOneCommand() {{auto lock = std::lock_guard{event_queue_lock_};//当前正在执行的action队列为空,但等待执行的事件队列不为空while (current_executing_actions_.empty() && !event_queue_.empty()) {for (const auto& action : actions_) {//从等待执行的事件队列头取出一个元素event, //然后调用action的CheckEvent检查此event是否匹配当前action //如果匹配,将这个action加入到正在执行的actions队列的队尾if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },event_queue_.front())) {current_executing_actions_.emplace(action.get());}}event_queue_.pop();}}// 如果当前没有执行的action直接结束if (current_executing_actions_.empty()) {return;}//从队列头取一个action(front不会使元素出队)auto action = current_executing_actions_.front();//如果是第一次执行这个actionif (current_command_ == 0) {std::string trigger_name = action->BuildTriggersString();LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()<< ":" << action->line() << ")";}//注释2,调用Action的ExecuteOneCommand方法,current_command_是个成员变量,标志着执行到了哪一行action->ExecuteOneCommand(current_command_);// If this was the last command in the current action, then remove// the action from the executing list.// If this action was oneshot, then also remove it from actions_.++current_command_;//如果current_command_等于action的commands数量,说明这个action已经全部执行完了if (current_command_ == action->NumCommands()) {current_executing_actions_.pop();//重置计数器current_command_ = 0;if (action->oneshot()) {auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),actions_.end());}}
}
system/core/init/action.cpp
void Action::ExecuteOneCommand(std::size_t command) const {// We need a copy here since some Command execution may result in// changing commands_ vector by importing .rc files through parserCommand cmd = commands_[command];ExecuteCommand(cmd);
}void Action::ExecuteCommand(const Command& command) const {android::base::Timer t;//注释3,调用Command的InvokeFunc方法,这一行是具体的执行auto result = command.InvokeFunc(subcontext_);auto duration = t.duration();// Any action longer than 50ms will be warned to user as slow operation//失败、超时或者debug版本都需要打印结果if (!result.has_value() || duration > 50ms ||android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {std::string trigger_name = BuildTriggersString();std::string cmd_str = command.BuildCommandString();LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_<< ":" << command.line() << ") took " << duration.count() << "ms and "<< (result.ok() ? "succeeded" : "failed: " + result.error().message());}
}Result<void> Command::InvokeFunc(Subcontext* subcontext) const {//从 /vendor 或 /oem 解析出来的rc文件都会走这里,涉及到selinux权限问题,Google为了保证安全,//对厂商定制的rc文件中的命令执行,以及由此启动的服务的权限都会有一定限制if (subcontext) {if (execute_in_subcontext_) {return subcontext->Execute(args_);}auto expanded_args = subcontext->ExpandArgs(args_);if (!expanded_args.ok()) {return expanded_args.error();}return RunBuiltinFunction(func_, *expanded_args, subcontext->context());}//注释4,系统原生的rc文件命令都会走这里,变量 func_函数其实是Command类的一个实现return RunBuiltinFunction(func_, args_, kInitContext);
}
/system/core/init/action.h
class Command {public://注释5,func_是BuiltinFunction类型Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,int line);Result<void> InvokeFunc(Subcontext* subcontext) const;std::string BuildCommandString() const;Result<void> CheckCommand() const;int line() const { return line_; }private:BuiltinFunction func_;bool execute_in_subcontext_;std::vector<std::string> args_;int line_;
};/system/core/init/builtins.h
//注释6
using BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;system/core/init/action.cpp
//注释7
Result<void> RunBuiltinFunction(const BuiltinFunction& function,const std::vector<std::string>& args, const std::string& context) {auto builtin_arguments = BuiltinArguments(context);builtin_arguments.args.resize(args.size());builtin_arguments.args[0] = args[0];for (std::size_t i = 1; i < args.size(); ++i) {auto expanded_arg = ExpandProps(args[i]);if (!expanded_arg.ok()) {return expanded_arg.error();}builtin_arguments.args[i] = std::move(*expanded_arg);}return function(builtin_arguments);
}
在注释1处调用ActionManager的ExecuteOneCommand来执行从rc文件中解析出来的指令。
在注释2处,也就是ActionManager的ExecuteOneCommand方法内部,进一步调用Action的ExecuteOneCommand。
在注释3处,也就是Action的ExecuteCommand方法内部,进一步调用Command的InvokeFunc方法。
在注释4处,也就是Command的InvokeFunc方法内部,将变量 func_函数作为参数,进一步调用RunBuiltinFunction方法。
然后结合注释5和注释6可知func_是BuiltinFunction类型,其实是一个函数。
进一步看注释7可知function是一个以BuiltinArguments为参数的std::function函数包装器模板,可以包装函数、函数指针、类成员函数指针或任意类型的函数对象。在Command对象new出来的时候构造函数就指定了这个func_,我们可以看一下Action::AddCommand方法。
system/core/init/action.cpp
Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {if (!function_map_) {return Error() << "no function map available";}auto map_result = function_map_->Find(args);if (!map_result.ok()) {return Error() << map_result.error();}commands_.emplace_back(map_result->function, map_result->run_in_subcontext, std::move(args),line);return {};
}
可以看到,是通过rc文件中的字符串去一个function_map_常量中查找得到的,而这个function_map_是在哪赋值的呢,答案是在SecondStageMain函数中
int SecondStageMain(int argc, char** argv) {...代码省略...//设置commands指令所对应的函数mapconst BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();Action::set_function_map(&function_map);...代码省略...
}
通过GetBuiltinFunctionMap函数获取commads 指令对应的map。
system/core/init/builtins.cpp
const BuiltinFunctionMap& GetBuiltinFunctionMap() {constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();// clang-format offstatic const BuiltinFunctionMap builtin_functions = {{"bootchart", {1, 1, {false, do_bootchart}}},{"chmod", {2, 2, {true, do_chmod}}},{"chown", {2, 3, {true, do_chown}}},{"class_reset", {1, 1, {false, do_class_reset}}},{"class_reset_post_data", {1, 1, {false, do_class_reset_post_data}}},{"class_restart", {1, 1, {false, do_class_restart}}},{"class_start", {1, 1, {false, do_class_start}}},{"class_start_post_data", {1, 1, {false, do_class_start_post_data}}},{"class_stop", {1, 1, {false, do_class_stop}}},{"copy", {2, 2, {true, do_copy}}},{"copy_per_line", {2, 2, {true, do_copy_per_line}}},{"domainname", {1, 1, {true, do_domainname}}},{"enable", {1, 1, {false, do_enable}}},{"exec", {1, kMax, {false, do_exec}}},{"exec_background", {1, kMax, {false, do_exec_background}}},{"exec_start", {1, 1, {false, do_exec_start}}},{"export", {2, 2, {false, do_export}}},{"hostname", {1, 1, {true, do_hostname}}},{"ifup", {1, 1, {true, do_ifup}}},{"init_user0", {0, 0, {false, do_init_user0}}},{"insmod", {1, kMax, {true, do_insmod}}},{"installkey", {1, 1, {false, do_installkey}}},{"interface_restart", {1, 1, {false, do_interface_restart}}},{"interface_start", {1, 1, {false, do_interface_start}}},{"interface_stop", {1, 1, {false, do_interface_stop}}},{"load_exports", {1, 1, {false, do_load_exports}}},{"load_persist_props", {0, 0, {false, do_load_persist_props}}},{"load_system_props", {0, 0, {false, do_load_system_props}}},{"loglevel", {1, 1, {false, do_loglevel}}},{"mark_post_data", {0, 0, {false, do_mark_post_data}}},{"mkdir", {1, 6, {true, do_mkdir}}},// TODO: Do mount operations in vendor_init.// mount_all is currently too complex to run in vendor_init as it queues action triggers,// imports rc scripts, etc. It should be simplified and run in vendor_init context.// mount and umount are run in the same context as mount_all for symmetry.{"mount_all", {0, kMax, {false, do_mount_all}}},{"mount", {3, kMax, {false, do_mount}}},{"perform_apex_config", {0, 0, {false, do_perform_apex_config}}},{"umount", {1, 1, {false, do_umount}}},{"umount_all", {0, 1, {false, do_umount_all}}},{"update_linker_config", {0, 0, {false, do_update_linker_config}}},{"readahead", {1, 2, {true, do_readahead}}},{"remount_userdata", {0, 0, {false, do_remount_userdata}}},{"restart", {1, 1, {false, do_restart}}},{"restorecon", {1, kMax, {true, do_restorecon}}},{"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},{"rm", {1, 1, {true, do_rm}}},{"rmdir", {1, 1, {true, do_rmdir}}},{"setprop", {2, 2, {true, do_setprop}}},{"setrlimit", {3, 3, {false, do_setrlimit}}},{"start", {1, 1, {false, do_start}}},{"stop", {1, 1, {false, do_stop}}},{"swapon_all", {0, 1, {false, do_swapon_all}}},{"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},{"symlink", {2, 2, {true, do_symlink}}},{"sysclktz", {1, 1, {false, do_sysclktz}}},{"trigger", {1, 1, {false, do_trigger}}},{"verity_update_state", {0, 0, {false, do_verity_update_state}}},{"wait", {1, 2, {true, do_wait}}},{"wait_for_prop", {2, 2, {false, do_wait_for_prop}}},{"write", {2, 2, {true, do_write}}},};// clang-format onreturn builtin_functions;
}
简单分析一下上面这段函数映射,如:rc 文件中 start 字段会对应执行 do_start 函数
启动服务
以下面一段rc脚本为例,我们看一下一个服务是怎么启动的
on zygote-start start zygote
首先这是一个action,当init进程在死循环中执行到ActionManager::ExecuteOneCommand方法时,检查到这个action刚好符合event_queue_队首的EventTrigger,便会执行这个action下面的commands。commands怎么执行在上面已经分析过了,我们去system/core/init/builtins.cpp里的map中找key-value对应关系,发现start对应着do_start函数:
/system/core/init/builtins.cpp
static Result<void> do_start(const BuiltinArguments& args) {// 拿到需要启动的服务Service* svc = ServiceList::GetInstance().FindService(args[1]);if (!svc) return Error() << "service " << args[1] << " not found";// 启动服务if (auto result = svc->Start(); !result.ok()) {return ErrorIgnoreEnoent() << "Could not start service: " << result.error();}return {};
}
ServiceList通过args[1]即定义的服务名去寻找之前解析好的service,并执行system/core/init/service.cpp中的Service::Start方法:
Result<void> Service::Start() {....// 省略无关代码pid_t pid = -1;//通过namespaces_.flags判断使用哪种方式创建进程if (namespaces_.flags) {pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);} else {pid = fork();}if (pid == 0) {//设置权限掩码umask(077);....// 省略无关代码//内部调用execv函数启动文件if (!ExpandArgsAndExecv(args_, sigstop_)) {PLOG(ERROR) << "cannot execv('" << args_[0]<< "'). See the 'Debugging init' section of init's README.md for tips";}_exit(127);}if (pid < 0) {pid_ = 0;return ErrnoError() << "Failed to fork";}....// 省略无关代码return {};
}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) {auto expanded_arg = ExpandProps(args[i]);if (!expanded_arg.ok()) {LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();}expanded_args[i] = *expanded_arg;c_strings.push_back(expanded_args[i].data());}c_strings.push_back(nullptr);if (sigstop) {kill(getpid(), SIGSTOP);}//调用execv函数,带参执行文件return execv(c_strings[0], c_strings.data()) == 0;
}
这里先fork(或clone)出了一个子进程,再在这个子进程中调用execv函数执行文件, 一个服务就启动了
fork、clone、vfork
这三个API的内部实际都是调用一个内核内部函数do_fork,只是填写的参数不同而已。
vfork,其实就是fork的部分过程,用以简化并提高效率。而fork与clone是区别的。fork是进程资源的完全复制,包括进程的PCB、线程的系统堆栈、进程的用户空间、进程打开的设备等。而在clone中其实只有前两项是被复制了的,后两项都与父进程共享。
在四项资源的复制中,用户空间是相对庞大的,如果完全复制则效率会很低。在Linux中采用的“写时复制”技术,也就是说,fork执行时并不真正复制用户空间的所有页面,而只是复制页面表。这样,无论父进程还是子进程,当发生用户空间的写操作时,都会引发“写复制”操作,而另行分配一块可用的用户空间,使其完全独立。这是一种提高效率的非常有效的方法。
而对于clone来说,它们连这些页面表都是与父进程共享,故而是真正意义上的共享,因此对共享数据的保护必须有上层应用来保证。