基于Android 9.0代碼進(jìn)行剖析,細(xì)解init進(jìn)程啟動的各個(gè)環(huán)節(jié)內(nèi)容
涉及到的源碼文件
system/core/init/init.cpp
system/core/init/init_first_stage.cpp
system/core/ueventd.cpp
system/core/init/selinux.cpp
system/core/init/sigchld_handler.cpp
system/core/init/property_service.cpp
本文主要的流程如下,init.rc解析及系統(tǒng)核心服務(wù)的啟動請參考:《Android啟動流程之二init.rc解析》 和《Android啟動流程之三 系統(tǒng)核心服務(wù)的啟動》

一、入口
init進(jìn)程是Linux系統(tǒng)中用戶空間的第一個(gè)進(jìn)程,PID=1,內(nèi)核啟動后會啟動init進(jìn)程并調(diào)用main方法作為入口,執(zhí)行init進(jìn)程應(yīng)該要履行的職責(zé)
掛載文件
解析系統(tǒng)配置的rc文件,執(zhí)行rc文件中的配置項(xiàng)
處理子進(jìn)程的終止信號
提供屬性服務(wù)
啟動zygote進(jìn)程
1.1 init主入口,main函數(shù)
源碼路徑
system/core/init/init.cpp
哇,這個(gè)main函數(shù)有點(diǎn)長,200多行??
int main(int argc, char** argv) {
//如果文件名是ueventd,執(zhí)行ueventd守護(hù)進(jìn)程的主函數(shù)
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
//如果文件名是ueventd,執(zhí)行watchdogd守護(hù)進(jìn)程的主函數(shù)
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
//如果文件名是ueventd,執(zhí)行subcontext守護(hù)進(jìn)程的主函數(shù)
if (argc > 1 && !strcmp(argv[1], "subcontext")) {
InitKernelLogging(argv);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
//通過讀取INIT_SECOND_STAGE來判斷是否是系統(tǒng)啟動的第一階段
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
//獲取當(dāng)前啟動時(shí)間
boot_clock::time_point start_time = boot_clock::now();
// Clear the umask.
//清除umask的設(shè)置,保證新創(chuàng)建的文件訪問權(quán)限不受umask的影響
// umask是Linux中關(guān)于文件訪問權(quán)限的操作命令,詳情可參考: https://www.cnblogs.com/sench/p/8933638.html
umask(0);
clearenv();
//配置環(huán)境變量
setenv("PATH", _PATH_DEFPATH, 1);
// 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.
//掛載tmpfs文件系統(tǒng),關(guān)于tmpfs可參考:https://blog.csdn.net/java211/article/details/52295348/
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
//掛載pts偽終端,pts介紹可以參考:http://blog.chinaunix.net/uid-22746363-id-384008.html
mkdir("/dev/pts", 0755);
//掛載socket文件系統(tǒng),Android核心socket存放在這里
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
//掛載proc文件系統(tǒng),proc是linux中虛擬文件系統(tǒng),可以作為Linux的控制中心,通過修改一些文件來改變
//內(nèi)核運(yùn)行狀態(tài),里面包括很多l(xiāng)inux核心工具,例如cmdline等,可參考:https://www.cnblogs.com/zydev/p/8728992.html
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);
//sysfs文件系統(tǒng)是一個(gè)類 似于proc文件系統(tǒng)的特殊文件系統(tǒng),用于將系統(tǒng)中的設(shè)備組織成層次結(jié)構(gòu),
//并向用戶模式程序提供詳細(xì)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)信息,可參考:https://blog.csdn.net/skyflying2012/article/details/11783847
mount("sysfs", "/sys", "sysfs", 0, NULL);
//掛載Selinux
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
//mknod可參考:https://blog.csdn.net/wsclinux/article/details/50907567
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
if constexpr (WORLD_WRITABLE_KMSG) {
mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
}
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000");
// /mnt/vendor is used to mount vendor-specific partitions that can not be
// part of the vendor partition, e.g. because they are mounted read-write.
mkdir("/mnt/vendor", 0755);
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
//init內(nèi)核log,為什么要使用kernel的log系統(tǒng),因?yàn)榇藭r(shí)Android系統(tǒng)的log還沒有啟動,所以需要使用kernel的log系統(tǒng)。
InitKernelLogging(argv);
LOG(INFO) << "init first stage started!";
//真正開始啟動第一次初始化
//在設(shè)備樹中掛載fstab指定的分區(qū),參考:init_first_stage.cpp
if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
//設(shè)置AVB信息到recovery中,參考:init_first_stage.cpp中的:setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
SetInitAvbVersionInRecovery();
// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
global_seccomp();
// Set up SELinux, loading the SELinux policy.
//初始化SeLinux,參考 1.2節(jié)
SelinuxSetupKernelLogging();
SelinuxInitialize();
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
//使用加載好的Selinux安全策略,重新設(shè)置init文件的安全權(quán)限
if (selinux_android_restorecon("/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /init failed";
}
//設(shè)置可以進(jìn)入第二階段初始化的標(biāo)記
setenv("INIT_SECOND_STAGE", "true", 1);
//記錄第一階段花費(fèi)的時(shí)間
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 };
//重新啟動init進(jìn)入main函數(shù)執(zhí)行第二階段
execv(path, args);
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
}
//以上代碼執(zhí)行完成第一階段的初始化,下面就會進(jìn)入第二階段的初始化
// 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.
// 在/dev目錄下創(chuàng)建一個(gè)隱藏的.booting文件表示正在進(jìn)行初始化,初始化完成后會把這個(gè)文件刪除
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
//初始化Android系統(tǒng)屬性,這個(gè)函數(shù)主要是創(chuàng)建一個(gè)共享區(qū)域來存儲屬性值,參考1.3節(jié)
property_init();
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
//DT的優(yōu)先級高于cmdline,因此需要先執(zhí)行DT的初始化解析,兩者詳細(xì)請參考 1.4節(jié)
process_kernel_dt();
process_kernel_cmdline();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
//如官方注釋,這里會讀取內(nèi)核的屬性,參考 1.5 節(jié)
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");
// Selinux第二階段的配置
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
// 初始化epoll
//epoll初始化為分為兩個(gè)階段,我們從函數(shù)命名就可以看得出來,
// 第一階段在init.cpp的main方法里面signal_handler_init函數(shù)調(diào)用之前,這里如果執(zhí)行失敗的話就直接退出了
//第二階段就在signal_handler_init初始化系統(tǒng)信號處理器時(shí)會執(zhí)行
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(FATAL) << "epoll_create1 failed";
}
//初始化子進(jìn)程退出信號處理 代碼:system/core/init/sigchld_handler.cpp,請參考 2.1 節(jié)
sigchld_handler_init();
if (!IsRebootCapable()) {
// If init does not have the CAP_SYS_BOOT capability, it is running in a container.
// In that case, receiving SIGTERM will cause the system to shut down.
InstallSigtermHandler();
}
//加載系統(tǒng)xxx.prop:system/core/init/property_service.cpp
property_load_boot_defaults();
export_oem_lock_status();
//start_property_service()函數(shù)創(chuàng)建了socket,然后監(jiān)聽,并且調(diào)用register_epoll_handler()函數(shù)把socket的fd放入epoll中
start_property_service();
set_usb_controller();
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
subcontexts = InitializeSubcontexts();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
//加載init.rc,參考 《Android啟動流程之init.rc解析》
LoadBootScripts(am, sm);
// 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();
//以下代碼把init.rc中的action添加到執(zhí)行隊(duì)列中
//將early-init的Action添加到action_queue中并執(zhí)行觸發(fā)器
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
//等待冷插拔設(shè)備初始化完成
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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
//設(shè)備組合鍵的初始化操作
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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
//如果是充電模式,不要掛載文件系統(tǒng)和啟動核心系統(tǒng)服務(wù)
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");
//循環(huán)等待事件的發(fā)生
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 || Service::is_exec_service_running())) {
//依次執(zhí)行action中有對應(yīng)command的執(zhí)行函數(shù),參考1.6 節(jié)
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
//如果有進(jìn)程需要重啟,執(zhí)行重啟操作,參考 1.7 節(jié)
auto next_process_restart_time = RestartProcesses();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_restart_time) {
epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_restart_time - boot_clock::now())
.count();
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初始化分為兩個(gè)階段,第一個(gè)階段主要是
掛載各種系統(tǒng)核心文件系統(tǒng)
初始化內(nèi)核log
加載系統(tǒng)核心屬性
加載SeLinux安全策略
加載完成Selinux安全策略后重新進(jìn)入init main函數(shù)執(zhí)行第二階段的初始化
1.2 初始化SeLinux
關(guān)于selinux的背景和詳細(xì)信息我們不在這里過多描述,可以參考這里 https://blog.csdn.net/qq_19923217/article/details/81240027
或者是Google官方文檔:https://source.android.com/security/selinux/images/SELinux_Treble.pdf
SelinuxSetupKernelLogging和SelinuxIntialize函數(shù)
- SelinuxSetupKernelLogging:
system/core/init/selinux.cpp
// This function sets up SELinux logging to be written to kmsg, to match init's logging.
void SelinuxSetupKernelLogging() {
selinux_callback cb;
//selinux 打印log所需要用到的callback
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
}
SelinuxSetupKernelLogging主要是初始化和設(shè)置SeLinux需要打印log時(shí)用到的callback,log信息會被寫入kmsg
- SelinuxIntialize
void SelinuxInitialize() {
//主要用于記錄SeLinux初始化的耗時(shí)
Timer t;
LOG(INFO) << "Loading SELinux policy";
//價(jià)值SeLinux策略
if (!LoadPolicy()) {
LOG(FATAL) << "Unable to load SELinux policy";
}
//Selinux工作模式包括:
//1、permissive,這個(gè)模式下所有的操作都被允許,但是如果違反權(quán)限規(guī)則的話會被記錄日志
//2、enforcing,所有操作都會進(jìn)行權(quán)限檢查
//以下代碼主要是設(shè)置SeLinux工作模式,我們可以使用adb shell getenforce 來獲取當(dāng)前處于哪種模式
bool kernel_enforcing = (security_getenforce() == 1);
bool is_enforcing = IsEnforcing();
if (kernel_enforcing != is_enforcing) {
if (security_setenforce(is_enforcing)) {
PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
}
}
if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
}
// init's first stage can't set properties, so pass the time to the second stage.
//init的第一階段無法寫入屬性,因此先記錄當(dāng)前耗時(shí),然后傳遞給第二階段
setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
}
接下來我們看一下LoadPolicy函數(shù),這會真正的去價(jià)值SeLinux安全策略
LoadPolicy會把安全策略傳遞給kernel,這樣kernel就有了安全策略的配置信息了,這里我們不做過多解釋,SeLinux
加載流程不在本文范圍內(nèi),后面可以通過其他專題來討論
bool LoadPolicy() {
return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
}
1.3 property_init
讀取系統(tǒng)屬性配置到共享區(qū)域。
system/core/init/property_service.cpp
void property_init() {
// 創(chuàng)建dev/_properties_存放系統(tǒng)屬性
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
//解析系統(tǒng)屬性
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";
}
}
void CreateSerializedPropertyInfo() {
auto property_infos = std::vector<PropertyInfoEntry>();
if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
&property_infos)) {
return;
}
// Don't check for failure here, so we always have a sane list of properties.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
&property_infos)) {
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
&property_infos);
}
} else {
if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
return;
}
if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
}
}
auto serialized_contexts = std::string();
auto error = std::string();
if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
&error)) {
LOG(ERROR) << "Unable to serialize property contexts: " << error;
return;
}
constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
PLOG(ERROR) << "Unable to write serialized property infos to file";
}
selinux_android_restorecon(kPropertyInfosPath, 0);
}
1.4 process dt 和cmdline
process_kernel_dt
該方法主要是打開dt目錄下的素有文件并讀取到系統(tǒng)屬性中,文件定義在/proc/device-tree/firmware/android/目錄下,
先判斷compatible文件中的內(nèi)容是否是android,firmware,如果不是就直接退出了。然后在循環(huán)讀取目錄下的文件,把文件
名作為屬性名,文件內(nèi)容作為屬性值進(jìn)行存儲
system/core/init/init.cpp
static void process_kernel_dt() {
//如果compatible的文件內(nèi)容不是android,firmware,直接返回
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
return;
}
//獲取并打開dt文件目錄,默認(rèn)是定義在system/core/init/util.cpp文件中的
//const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/")
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
if (!dir) return;
std::string dt_file;
struct dirent *dp;
//循環(huán)讀取dt目錄中的文件
while ((dp = readdir(dir.get())) != NULL) {
if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
continue;
}
std::string file_name = get_android_dt_dir() + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
//文件名作為屬性名,文件內(nèi)容作為屬性值
property_set("ro.boot."s + dp->d_name, dt_file);
}
}
process_kernel_cmdline函數(shù)
該方法會執(zhí)行兩次import_kernel_cmdline函數(shù),
第一遍主要是判斷是否在qemu中,第一遍第一個(gè)參數(shù)傳遞false,表示不是qemu
第二遍才是真正的讀取proc/cmdline文件中的內(nèi)容并轉(zhuǎn)換為內(nèi)核屬性,第二遍第一個(gè)參數(shù)傳遞true,表示已經(jīng)是qemu,并
傳遞import_kernel_nv函數(shù)指針讀取系統(tǒng)屬性
static void process_kernel_cmdline() {
// The first pass does the common stuff, and finds if we are in qemu.
// The second pass is only necessary for qemu to export all kernel params
// as properties.
import_kernel_cmdline(false, import_kernel_nv);
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
import_kernel_cmdline函數(shù)
system/core/init/util.cpp
void import_kernel_cmdline(bool in_qemu,
const std::function<void(const std::string&, const std::string&, bool)>& fn) {
std::string cmdline;
//讀取proc/cmdline中的內(nèi)容到cmdline字符串中
android::base::ReadFileToString("/proc/cmdline", &cmdline);
//循環(huán)讀取屬性,讀取方式為,將字符串cmdline的內(nèi)容按空格分隔成字符串?dāng)?shù)組,并循環(huán)遍歷
//這個(gè)數(shù)組,然后調(diào)用fn設(shè)置屬性,這里fn就是在process_kernel_cmdline方法中傳遞進(jìn)來的import_kernel_nv
for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
std::vector<std::string> pieces = android::base::Split(entry, "=");
if (pieces.size() == 2) {
fn(pieces[0], pieces[1], in_qemu);
}
}
}
import_kernel_nv函數(shù)
system/core/init/init.cpp
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
if (key.empty()) return;
//import_kernel_cmdline第一次執(zhí)行的時(shí)候傳遞過來的for_emulator為false,第二次為true
//因此這里在第一遍調(diào)用的時(shí)候不會執(zhí)行
if (for_emulator) {
// In the emulator, export any kernel option with the "ro.kernel." prefix.
property_set("ro.kernel." + key, value);
return;
}
if (key == "qemu") {
strlcpy(qemu, value.c_str(), sizeof(qemu));
} else if (android::base::StartsWith(key, "androidboot.")) {
property_set("ro.boot." + key.substr(12), value);
}
}
1.5 export_kernel_boot_props
static void export_kernel_boot_props() {
struct {
const char *src_prop;
const char *dst_prop;
const char *default_value;
} prop_map[] = {
{ "ro.boot.serialno", "ro.serialno", "", },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
};
// unknown作為默認(rèn)值,如果沒有寫入指定的value的話就用unknown作為value
for (size_t i = 0; i < arraysize(prop_map); i++) {
std::string value = GetProperty(prop_map[i].src_prop, "");
property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
}
}
1.6 ExecuteOneCommand
void ActionManager::ExecuteOneCommand() {
// Loop through the event queue until we have an action to execute
while (current_executing_actions_.empty() && !event_queue_.empty()) {
for (const auto& 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();
}
//current_executing_actions_ 為空則退出
if (current_executing_actions_.empty()) {
return;
}
//獲取current_executin_actions_ 頭部
auto action = current_executing_actions_.front();
if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
<< ":" << action->line() << ")";
}
//system/core/init/action.cpp
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_.
//如果這是最后一個(gè)action或者是一個(gè)oneshot的action,執(zhí)行完成后直接移除隊(duì)列
++current_command_;
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));
}
}
}
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 parser
//為了避免因?yàn)榻馕?rc文件導(dǎo)致command_隊(duì)列被改變,所以要先拷貝一份
Command cmd = commands_[command];
//執(zhí)行command
ExecuteCommand(cmd);
}
void Action::ExecuteCommand(const Command& command) const {
android::base::Timer t;
//調(diào)用Command的invokeFunc函數(shù)執(zhí)行
auto result = command.InvokeFunc(subcontext_);
auto duration = t.duration();
// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
// device, such as '/sys/class/leds/jogball-backlight/brightness'. As of this writing, there
// are 198 such failures on bullhead. Instead of spamming the log reporting them, we do not
// report such failures unless we're running at the DEBUG log level.
bool report_failure = !result.has_value();
if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
result.error_errno() == ENOENT) {
report_failure = false;
}
// Any action longer than 50ms will be warned to user as slow operation
if (report_failure || 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 ? "succeeded" : "failed: " + result.error_string());
}
}
1.7 重啟進(jìn)程
static std::optional<boot_clock::time_point> RestartProcesses() {
std::optional<boot_clock::time_point> next_process_restart_time;
//遍歷service列表進(jìn)行重啟
for (const auto& s : ServiceList::GetInstance()) {
//如果flag不是SVC_RESTARTING,取下一個(gè)
if (!(s->flags() & SVC_RESTARTING)) continue;
auto restart_time = s->time_started() + 5s;
if (boot_clock::now() > restart_time) {
//啟動進(jìn)程
if (auto result = s->Start(); !result) {
LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
}
} else {
if (!next_process_restart_time || restart_time < *next_process_restart_time) {
next_process_restart_time = restart_time;
}
}
}
return next_process_restart_time;
}
二、信號處理
sigchld_handler_init主要是子進(jìn)程死掉后,父進(jìn)程中會接收到一個(gè)SIGCHLD信號,init進(jìn)程接收到這個(gè)信號后會將已經(jīng)死了的子進(jìn)程移除,避免成為
僵尸進(jìn)程占用系統(tǒng)程序表的空間。
2.1 sigchld_handler_init
system/core/init/sigchld_handler.cpp
void sigchld_handler_init() {
// Create a signalling mechanism for SIGCHLD.
int s[2];
//創(chuàng)建用于接收和寫入信號的兩個(gè)socket
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
PLOG(FATAL) << "socketpair failed in sigchld_handler_init";
}
signal_write_fd = s[0];
signal_read_fd = s[1];
// Write to signal_write_fd if we catch SIGCHLD.
//接收到SIGCHLD信號后就寫入signal_write_fd
struct sigaction act;
memset(&act, 0, sizeof(act));
//SIGCHLD_handler會被存放在sigaction數(shù)據(jù)結(jié)構(gòu)里面,負(fù)責(zé)處理SIGCHLD信號
act.sa_handler = SIGCHLD_handler;
//用于標(biāo)記只有在子進(jìn)程死掉用才接收信息
act.sa_flags = SA_NOCLDSTOP;
//注冊將需要監(jiān)聽的信號SIGCHLD及對應(yīng)的處理器
sigaction(SIGCHLD, &act, 0);
//進(jìn)入waitid處理子進(jìn)程是否退出的情況
ReapAnyOutstandingChildren();
//注冊回調(diào)用于監(jiān)聽信號,其中handle_signal是函數(shù)指針
register_epoll_handler(signal_read_fd, handle_signal);
}
Linux系統(tǒng)中進(jìn)程之間可以發(fā)送信息來實(shí)現(xiàn)進(jìn)程間通訊,這里的這種機(jī)制可以叫做 信號,每個(gè)進(jìn)程在處理信號時(shí)是需要被注冊為處理者。
這里的兩個(gè)函數(shù)中,SIGCHILD_handler負(fù)責(zé)寫入信號,handle_signal負(fù)責(zé)讀取信號。子進(jìn)程在死掉后會產(chǎn)生SIGCHILD信號并寫入signal_write_fd,信號通過
socket發(fā)送到父進(jìn)程,父進(jìn)程讀取信號到signal_read_fd
//寫入信號
static void SIGCHLD_handler(int) {
//向signal_write_fd中寫入1
if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
PLOG(ERROR) << "write(signal_write_fd) failed";
}
}
//讀取信號
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
read(signal_read_fd, buf, sizeof(buf));
//這里只要是回收死掉的子進(jìn)程
ReapAnyOutstandingChildren();
}
2.2 ReapAnyOutstandingChildren函數(shù)
void ReapAnyOutstandingChildren() {
while (ReapOneProcess()) {
}
}
static bool ReapOneProcess() {
siginfo_t siginfo = {};
// This returns a zombie pid or informs us that there are no zombies left to be reaped.
// It does NOT reap the pid; that is done below.
if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
PLOG(ERROR) << "waitid failed";
return false;
}
auto pid = siginfo.si_pid;
if (pid == 0) return false;
// At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
// whenever the function returns from this point forward.
// We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
// want the pid to remain valid throughout that (and potentially future) usages.
auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
std::string name;
std::string wait_string;
Service* service = nullptr;
if (PropertyChildReap(pid)) {
name = "Async property child";
} else if (SubcontextChildReap(pid)) {
name = "Subcontext";
} else {
//find service via pid
service = ServiceList::GetInstance().FindService(pid, &Service::pid);
if (service) {
name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
if (service->flags() & SVC_EXEC) {
auto exec_duration = boot_clock::now() - service->time_started();
auto exec_duration_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
}
} else {
name = StringPrintf("Untracked pid %d", pid);
}
}
if (siginfo.si_code == CLD_EXITED) {
LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
} else {
LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
}
if (!service) return true;
//回收子進(jìn)程,參考system/core/init/service.cpp:: Reap
service->Reap(siginfo);
if (service->flags() & SVC_TEMPORARY) {
ServiceList::GetInstance().RemoveService(*service);
}
return true;
}
service.cpp :: Reap
void Service::Reap(const siginfo_t& siginfo) {
// flag為ONESHOT或者RESTART時(shí)直接kill掉進(jìn)程組
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL);
}
// Remove any descriptor resources we may have created.
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
for (const auto& f : reap_callbacks_) {
f(siginfo);
}
if (flags_ & SVC_EXEC) UnSetExec();
if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
flags_ &= (~SVC_RUNNING);
start_order_ = 0;
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
flags_ |= SVC_DISABLED;
}
// Disabled and reset processes do not get restarted automatically.
if (flags_ & (SVC_DISABLED | SVC_RESET)) {
NotifyStateChange("stopped");
return;
}
// If we crash > 4 times in 4 minutes, reboot into recovery.
boot_clock::time_point now = boot_clock::now();
if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
if (now < time_crashed_ + 4min) {
if (++crash_count_ > 4) {
LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
}
} else {
time_crashed_ = now;
crash_count_ = 1;
}
}
flags_ &= (~SVC_RESTART);
flags_ |= SVC_RESTARTING;
// Execute all onrestart commands for this service.
onrestart_.ExecuteAllCommands();
//設(shè)置對應(yīng)的service為restarting狀態(tài)
NotifyStateChange("restarting");
return;
}
參考: