init進程,它是一個由內核啓動的用戶級進程,當Linux內核啓動以後,運行的第一個進程是init,這個進程是一個守護進程,確切的說,它是Linux系統中用戶控件的第一個進程,因此它的進程號是1。它的生命週期貫穿整個linux 內核運行的始終, linux中全部其它的進程的共同始祖均爲init進程。html
Android 版本 | 關鍵類 | 路徑 |
---|---|---|
8.1 | init.rc | system/core/rootdir/init.rc |
8.1 | init.cpp | system/core/init/init.cpp |
8.1 | property_service.cpp | system/core/init/property_service.cpp |
8.1 | init_parser.h | system/core/init/init_parser.h |
8.1 | init_parser.cpp | system/core/init/init_parser.cpp |
8.1 | log.cpp | system/core/init/log.cpp |
8.1 | logging.cpp | system/core/base/logging.cpp |
8.1 | property_service.cpp | system/core/init/property_service.cpp |
8.1 | signal_handler.cpp | system/core/init/signal_handler.cpp |
8.1 | service.cpp | system/core/init/service.cpp |
8.1 | Action.cpp | system/core/init/Action.cpp |
8.1 | builtins.cpp | system/core/init/builtins.cpp |
1. 按下電源系統啓動
當電源按下時引導芯片代碼開始從預約義的地方(固化在ROM)開始執行,加載引導程序Bootloader到RAM,而後執行。
2. 引導程序Bootloader
引導程序是在Android操做系統開始運行前的一個小程序,它的主要做用是把系統OS拉起來並運行。
3. linux內核啓動
內核啓動時,設置緩存、被保護存儲器、計劃列表,加載驅動。當內核完成系統設置,它首先在系統文件中尋找」init」文件,而後啓動root進程或者系統的第一個進程。
4. init進程啓動
✨ 這就是咱們接下來要討論的內容 ✨java
Android init進程的入口文件在system/core/init/init.cpp中,因爲init是命令行程序,因此分析init.cpp首先應從main函數開始:linux
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ //根據參數,判斷是否須要啓動ueventd和watchdogd if (!strcmp(basename(argv[0]), "ueventd")) { // 啓動ueventd return ueventd_main(argc, argv); } if (!strcmp(basename(argv[0]), "watchdogd")) { // 啓動watchdogd return watchdogd_main(argc, argv); } if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); // 若緊急重啓,則安裝對應的消息處理器 } add_environment("PATH", _PATH_DEFPATH); // 添加環境變量 ... ... }
int main(int argc, char** argv) { /* 01. 判斷及增長環境變量 */ bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr); if (is_first_stage) { // 判斷是不是系統啓動的第一階段(第一次進入:true) boot_clock::time_point start_time = boot_clock::now(); // 用於記錄啓動時間 // Clear the umask. umask(0); // 清除屏蔽字(file mode creation mask),保證新建的目錄的訪問權限不受屏蔽字影響 // 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"); // 掛載tmpfs文件系統 mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); // 掛載devpts文件系統 #define MAKE_STR(x) __STRING(x) mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)); // 掛載proc文件系統 // Don't expose the raw commandline to unprivileged processes. chmod("/proc/cmdline", 0440); // 8.0新增, 收緊了cmdline目錄的權限 gid_t groups[] = { AID_READPROC }; // 8.0新增,增長了個用戶組 setgroups(arraysize(groups), groups); mount("sysfs", "/sys", "sysfs", 0, NULL); // 掛載sysfs文件系統 mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL); // 8.0新增 mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)); // 提早建立了kmsg設備節點文件,用於輸出log信息 mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)); mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)); ... ... }
如上所示,該部分主要用於建立和掛載啓動所需的文件目錄。須要注意的是,在編譯Android系統源碼時,在生成的根文件系統中, 並不存在這些目錄,它們是系統運行時的目錄,即當系統終止時,就會消失。android
四類文件系統:ios
tmpfs:一種虛擬內存文件系統,它會將全部的文件存儲在虛擬內存中,若是你將tmpfs文件系統卸載後,那麼其下的全部的內容將不復存在。tmpfs既可使用RAM,也可使用交換分區,會根據你的實際須要而改變大小。tmpfs的速度很是驚人,畢竟它是駐留在RAM中的,即便用了交換分區,性能仍然很是卓越。因爲tmpfs是駐留在RAM的,所以它的內容是不持久的。斷電後,tmpfs的內容就消失了,這也是被稱做tmpfs的根本緣由。c++
devpts:爲僞終端提供了一個標準接口,它的標準掛接點是/dev/ pts。只要pty的主複合設備/dev/ptmx被打開,就會在/dev/pts下動態的建立一個新的pty設備文件。編程
proc:一個很是重要的虛擬文件系統,它能夠看做是內核內部數據結構的接口,經過它咱們能夠得到系統的信息,同時也可以在運行時修改特定的內核參數。小程序
sysfs:與proc文件系統相似,也是一個不佔有任何磁盤空間的虛擬文件系統。它一般被掛接在/sys目錄下。sysfs文件系統是Linux2.6內核引入的,它把鏈接在系統上的設備和總線組織成爲一個分級的文件,使得它們能夠在用戶空間存取。緩存
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立並掛載相關的文件系統 */ if (is_first_stage) { ... ... // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually // talk to the outside world... InitKernelLogging(argv); ... ... } ... ...
跟蹤InitKernelLogging(): system/core/init/log.cpp安全
void InitKernelLogging(char* argv[]) { // Make stdin/stdout/stderr all point to /dev/null. int fd = open("/sys/fs/selinux/null", O_RDWR); if (fd == -1) { // 若開啓失敗,則記錄log int saved_errno = errno; android::base::InitLogging(argv, &android::base::KernelLogger); errno = saved_errno; PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null"; } dup2(fd, 0); // dup2函數的做用是用來複制一個文件的描述符, 一般用來重定向進程的stdin、stdout和stderr dup2(fd, 1); // 它的函數原形是:int dup2(int oldfd, int targetfd),該函數執行後,targetfd將變成oldfd的複製品 dup2(fd, 2); // 所以這邊的過程其實就是:建立出__null__設備後,將0、一、2綁定到__null__設備上 if (fd > 2) close(fd); // 因此init進程調用InitKernelLogging函數後,經過標準的輸入輸出沒法輸出信息 android::base::InitLogging(argv, &android::base::KernelLogger); }
跟蹤InitLogging():system/core/base/logging.cpp
// 設置KernelLogger void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { //設置logger SetLogger(std::forward<LogFunction>(logger)); SetAborter(std::forward<AbortFunction>(aborter)); if (gInitialized) { return; } gInitialized = true; // Stash the command line for later use. We can use /proc/self/cmdline on // Linux to recover this, but we don't have that luxury on the Mac/Windows, // and there are a couple of argv[0] variants that are commonly used. if (argv != nullptr) { std::lock_guard<std::mutex> lock(LoggingLock()); ProgramInvocationName() = basename(argv[0]); } const char* tags = getenv("ANDROID_LOG_TAGS"); if (tags == nullptr) { return; } // 根據TAG決定最小記錄等級 std::vector<std::string> specs = Split(tags, " "); for (size_t i = 0; i < specs.size(); ++i) { // "tag-pattern:[vdiwefs]" std::string spec(specs[i]); if (spec.size() == 3 && StartsWith(spec, "*:")) { switch (spec[2]) { case 'v': gMinimumLogSeverity = VERBOSE; continue; ... ... } } LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags << ")"; } }
當須要輸出日誌時,KernelLogger函數就會被調用:
#if defined(__linux__) void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag, const char*, unsigned int, const char* msg) { ... ... // 打開log節點 static int klog_fd = TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)); if (klog_fd == -1) return; // 決定log等級 int level = kLogSeverityToKernelLogLevel[severity]; // The kernel's printk buffer is only 1024 bytes. // TODO: should we automatically break up long lines into multiple lines? // Or we could log but with something like "..." at the end? char buf[1024]; size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg); if (size > sizeof(buf)) { size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n", level, tag, size); } iovec iov[1]; iov[0].iov_base = buf; iov[0].iov_len = size; // 經過iovec將log發送到dev/kmsg TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1)); } #endif
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ if (is_first_stage) { ... ... // 掛載特定的分區設備 if (!DoFirstStageMount()) { LOG(ERROR) << "Failed to mount required partitions early ..."; panic(); // panic會嘗試reboot } } ... ...
跟蹤DoFirstStageMount():system/core/init/init_first_stage.cpp
// Mounts partitions specified by fstab in device tree. bool DoFirstStageMount() { // Skips first stage mount if we're in recovery mode. if (IsRecoveryMode()) { LOG(INFO) << "First stage mount skipped (recovery mode)"; return true; } // Firstly checks if device tree fstab entries are compatible. if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) { LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)"; return true; } // 知足上述條件時,就會調用FirstStageMount的DoFirstStageMount函數 std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create(); if (!handle) { LOG(ERROR) << "Failed to create FirstStageMount"; return false; } // 主要是初始化特定設備並掛載 return handle->DoFirstStageMount(); }
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ if (is_first_stage) { ... ... // 此處應該是初始化安全框架:Android Verified Boot // AVB主要用於防止系統文件自己被篡改,還包含了防止系統回滾的功能, // 以避免有人試圖回滾系統並利用之前的漏洞 SetInitAvbVersionInRecovery(); // Set up SELinux, loading the SELinux policy. selinux_initialize(true); // 調用selinux_initialize啓動SELinux ... ... } ... ...
跟蹤selinux_initialize():
static void selinux_initialize(bool in_kernel_domain) { Timer t; selinux_callback cb; cb.func_log = selinux_klog_callback; // 用於打印log的回調函數 selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; // 用於檢查權限的回調函數 selinux_set_callback(SELINUX_CB_AUDIT, cb); // init進程的運行是區分用戶態和內核態的,first_stage運行在內核態 if (in_kernel_domain) { LOG(INFO) << "Loading SELinux policy"; // 用於加載sepolicy文件 // 該函數最終將sepolicy文件傳遞給kernel,這樣kernel就有了安全策略配置文件,後續的MAC才能開展起來。 if (!selinux_load_policy()) { panic(); } bool kernel_enforcing = (security_getenforce() == 1); // 內核中讀取的信息 bool is_enforcing = selinux_is_enforcing(); // 命令行中獲得的數據 // 用於設置selinux的工做模式。selinux有兩種工做模式: // 一、」permissive」,全部的操做都被容許(即沒有MAC),可是若是違反權限的話,會記錄日誌 // 二、」enforcing」,全部操做都會進行權限檢查。在通常的終端中,應該工做於enforing模式 if (kernel_enforcing != is_enforcing) { if (security_setenforce(is_enforcing)) { PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false"); security_failure(); // 將重啓進入recovery mode } } std::string err; if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) { LOG(ERROR) << err; security_failure(); } // init's first stage can't set properties, so pass the time to the second stage. setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1); } else { selinux_init_all_handles(); // 在second stage調用時,初始化全部的handle } }
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ if (is_first_stage) { ... ... // 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) { // 按selinux policy要求,從新設置init文件屬性 PLOG(ERROR) << "restorecon failed"; security_failure(); // 失敗的話會reboot } 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); // 再次調用init的main函數,啓動用戶態的init進程 // 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(); // 內核態的進程不該該退出,若退出則會重啓 } /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ ... ... }
上面全部的源碼咱們都是圍繞第一階段分析(is_first_stage),自此第一階段結束,會復位一些信息,並設置一些環境變量,最後啓動用戶態的init進程,進入init第二階段。
init進程的第二階段仍然從main函數開始入手(繼續分析main函數剩餘源碼)
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ // 一樣進行一些判斷及環境變量設置的工做 ... ... // 如今 is_first_stage 爲 false 了 bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr); // 這部分工做再也不執行了 if (is_first_stage) { ........... } // At this point we're in the second stage of init. InitKernelLogging(argv); // 一樣屏蔽標準輸入輸出及定義Kernel logger 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); // 最後調用syscall,設置安全相關的值 // Indicate that booting is in progress to background fw loaders, etc. close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); // 這裏的功能相似於「鎖」 ... ... property_init(); // 初始化屬性域 --> 定義於system/core/init/property_service.cpp // 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); ... ... }
這部分代碼主要的工做應該就是調用 property_init 初始化屬性域,而後設置各類屬性。
在Android平臺中,爲了讓運行中的全部進程共享系統運行時所須要的各類設置值,系統開闢了屬性存儲區域,並提供了訪問該區域的API。
跟蹤property_init():system/core/init/property_service.cpp
void property_init() { if (__system_property_area_init()) { // 最終調用_system_property_area_init函數初始化屬性域 LOG(ERROR) << "Failed to initialize property area"; exit(1); } }
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ // Clean up our environment. unsetenv("INIT_SECOND_STAGE"); unsetenv("INIT_STARTED_AT"); // 清除掉以前使用過的環境變量 unsetenv("INIT_SELINUX_TOOK"); unsetenv("INIT_AVB_VERSION"); ... ... }
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ /* 02. 清空環境變量 */ // Now set up SELinux for second stage. selinux_initialize(false); selinux_restore_context(); // 再次完成selinux相關的工做
咱們發如今init進程的第一階段,也調用了selinux_initialize函數,那麼二者有什麼區別?
init進程第一階段主要加載selinux相關的策略,而第二階段調用selinux_initialize僅僅註冊一些處理器。
咱們跟下selinux_initialize():
static void selinux_initialize(bool in_kernel_domain) { Timer t; selinux_callback cb; cb.func_log = selinux_klog_callback; selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); if (in_kernel_domain) { ... ... // 這邊就是第一階段的工做 } else { selinux_init_all_handles(); // 這邊就是第二階段的工做:註冊處理器 } }
再來看一下selinux_restore_context():主要是按 selinux policy 要求,從新設置一些文件的屬性。
// The files and directories that were created before initial sepolicy load or // files on ramdisk need to have their security context restored to the proper // value. This must happen before /dev is populated by ueventd. // 如註釋所述,如下文件在selinux被加載前就建立了 // 因而,在selinux啓動後,須要從新設置一些屬性 static void selinux_restore_context() { LOG(INFO) << "Running restorecon..."; selinux_android_restorecon("/dev", 0); selinux_android_restorecon("/dev/kmsg", 0); selinux_android_restorecon("/dev/socket", 0); selinux_android_restorecon("/dev/random", 0); selinux_android_restorecon("/dev/urandom", 0); selinux_android_restorecon("/dev/__properties__", 0); selinux_android_restorecon("/plat_file_contexts", 0); selinux_android_restorecon("/nonplat_file_contexts", 0); selinux_android_restorecon("/plat_property_contexts", 0); selinux_android_restorecon("/nonplat_property_contexts", 0); selinux_android_restorecon("/plat_seapp_contexts", 0); selinux_android_restorecon("/nonplat_seapp_contexts", 0); selinux_android_restorecon("/plat_service_contexts", 0); selinux_android_restorecon("/nonplat_service_contexts", 0); selinux_android_restorecon("/plat_hwservice_contexts", 0); selinux_android_restorecon("/nonplat_hwservice_contexts", 0); selinux_android_restorecon("/sepolicy", 0); selinux_android_restorecon("/vndservice_contexts", 0); selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE); selinux_android_restorecon("/dev/device-mapper", 0); selinux_android_restorecon("/sbin/mke2fs_static", 0); selinux_android_restorecon("/sbin/e2fsdroid_static", 0); }
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ /* 02. 清空環境變量 */ /* 03. 完成SELinux相關工做 */ epoll_fd = epoll_create1(EPOLL_CLOEXEC); // 調用epoll_create1建立epoll句柄 if (epoll_fd == -1) { PLOG(ERROR) << "epoll_create1 failed"; exit(1); } ... ... }
在linux的網絡編程中,很長的時間都在使用 select 來作事件觸發。在linux新的內核中,有了一種替換它的機制,就是 epoll。
相比於select,epoll最大的好處在於它不會隨着監聽fd數目的增加而下降效率。由於在內核中的 select 實現中,它是採用輪詢來處理的,輪詢的fd數目越多,天然耗時越多。
epoll機制通常使用epoll_create(int size)函數建立epoll句柄,size用來告訴內核這個句柄可監聽的fd的數目。
注意這個參數不一樣於select()中的第一個參數,在select中需給出最大監聽數加1的值。
此外,當建立好epoll句柄後,它就會佔用一個fd值,在linux下若是查看/proc/進程id/fd/,可以看到建立出的fd,所以在使用完epoll後,必須調用close()關閉,不然可能致使fd被耗盡。
上述代碼使用的epoll_create1(EPOLL_CLOEXEC)來建立epoll句柄,該標誌位表示生成的epoll fd具備「執行後關閉」特性。
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ /* 02. 清空環境變量 */ /* 03. 完成SELinux相關工做 */ /* 04. 建立epoll句柄 */ signal_handler_init(); // 裝載子進程信號處理器 }
init是一個守護進程,爲了防止init的子進程成爲殭屍進程(zombie process),須要init在子進程在結束時獲取子進程的結束碼,經過結束碼將程序表中的子進程移除,防止成爲殭屍進程的子進程佔用程序表的空間(程序表的空間達到上限時,系統就不能再啓動新的進程了,會引發嚴重的系統問題)。
在linux當中,父進程是經過捕捉 SIGCHLD 信號來得知子進程運行結束的狀況,此處init進程調用 signal_handler_init 的目的就是捕獲子進程結束的信號。
咱們跟蹤下signal_handler_init():
void signal_handler_init() { // Create a signalling mechanism for SIGCHLD. int s[2]; // 利用socketpair建立出已經鏈接的兩個socket,分別做爲信號的讀、寫端 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) { PLOG(ERROR) << "socketpair failed"; exit(1); } signal_write_fd = s[0]; signal_read_fd = s[1]; // Write to signal_write_fd if we catch SIGCHLD. struct sigaction act; memset(&act, 0, sizeof(act)); // 信號處理器對應的執行函數爲SIGCHLD_handler // 被存在sigaction結構體中,負責處理SIGCHLD消息 act.sa_handler = SIGCHLD_handler; act.sa_flags = SA_NOCLDSTOP; // 調用信號安裝函數sigaction,將監聽的信號及對應的信號處理器註冊到內核中 sigaction(SIGCHLD, &act, 0); // 用於終止出現問題的子進程 ServiceManager::GetInstance().ReapAnyOutstandingChildren(); // 註冊信號處理函數handle_signal register_epoll_handler(signal_read_fd, handle_signal); }
在深刻分析代碼前,咱們須要瞭解一些基本概念:Linux進程經過互相發送消息來實現進程間的通訊,這些消息被稱爲「信號」。每一個進程在處理其它進程發送的信號時都要註冊處理者,處理者被稱爲信號處理器。
注意到sigaction結構體的sa_flags爲SA_NOCLDSTOP。因爲系統默認在子進程暫停時也會發送信號SIGCHLD,init須要忽略子進程在暫停時發出的SIGCHLD信號,所以將act.sa_flags 置爲SA_NOCLDSTOP,該標誌位表示僅當進程終止時才接受SIGCHLD信號。
接下來,咱們分步驟詳細瞭解一下signal_handler_init具體的工做流程。
// system/core/init/signal_handler.cpp static void SIGCHLD_handler(int) { if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) { PLOG(ERROR) << "write(signal_write_fd) failed"; } }
從上面代碼咱們知道,init進程是全部進程的父進程,當其子進程終止產生SIGCHLD信號時,SIGCHLD_handler將對signal_write_fd執行寫操做。因爲socketpair的綁定關係,這將觸發信號對應的signal_read_fd收到數據。
根據前文的代碼咱們知道,在裝載信號監聽器的最後,signal_handler_init調用了register_epoll_handler,其代碼以下所示,注意傳入的參數分別爲signal_read_fd和handle_signal:
// system/core/init/init.cpp void register_epoll_handler(int fd, void (*fn)()) { epoll_event ev; ev.events = EPOLLIN; ev.data.ptr = reinterpret_cast<void*>(fn); // epoll_fd增長一個監聽對象fd,fd上有數據到來時,調用fn處理 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { PLOG(ERROR) << "epoll_ctl failed"; } }
根據代碼不難看出:當epoll句柄監聽到signal_read_fd中有數據可讀時,將調用handle_signal進行處理。
至此,結合上文咱們知道:當init進程調用signal_handler_init後,一旦收到子進程終止帶來的SIGCHLD消息後,將利用信號處理者SIGCHLD_handler向signal_write_fd寫入信息;因爲綁定的關係,epoll句柄將監聽到signal_read_fd收到消息,因而將調用handle_signal進行處理。
// system/core/init/signal_handler.cpp static void handle_signal() { // Clear outstanding requests. char buf[32]; read(signal_read_fd, buf, sizeof(buf)); ServiceManager::GetInstance().ReapAnyOutstandingChildren(); }
從代碼中能夠看出,handle_signal只是清空signal_read_fd中的數據,而後調用ServiceManager::GetInstance().ReapAnyOutstandingChildren()。
ServiceManager定義於 system/core/init/service.cpp 中,是一個單例對象:
... ... ServiceManager::ServiceManager() { } ServiceManager& ServiceManager::GetInstance() { static ServiceManager instance; return instance; } ... ... void ServiceManager::ReapAnyOutstandingChildren() { while (ReapOneProcess()) { } } ... ...
如上所示,ReapAnyOutstandingChildren函數實際上調用了ReapOneProcess。咱們結合代碼,看看ReapOneProcess的具體工做。
bool ServiceManager::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. //用waitid函數獲取狀態發生變化的子進程pid //waitid的標記爲WNOHANG,即非阻塞,返回爲正值就說明有進程掛掉了 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)); }); if (PropertyChildReap(pid)) { return true; } // 利用FindServiceByPid函數,找到pid對應的服務 // FindServiceByPid主要經過輪詢解析init.rc生成的service_list,找到pid與參數一致的srvc Service* svc = FindServiceByPid(pid); ... ... // 輸出服務結束的緣由 if (!svc) { // 沒有找到,說明已經結束了 return true; } svc->Reap(); // 根據svc的類型,決定後續的處理方式 if (svc->flags() & SVC_EXEC) { exec_waiter_.reset(); // 可執行服務則重置對應的waiter } if (svc->flags() & SVC_TEMPORARY) { RemoveService(*svc); // 移除臨時服務 } return true; }
void Service::Reap() { // 清理未攜帶SVC_ONESHOT 或 攜帶了SVC_RESTART標誌的srvc的進程組 if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) { KillProcessGroup(SIGKILL); } // Remove any descriptor resources we may have created. // 清除srvc中建立出的任意描述符 std::for_each(descriptors_.begin(), descriptors_.end(), std::bind(&DescriptorInfo::Clean, std::placeholders::_1)); // 清理工做完畢後,後面決定是否重啓機器或重啓服務 // TEMP服務不用參與這種判斷 if (flags_ & SVC_TEMPORARY) { return; } pid_ = 0; flags_ &= (~SVC_RUNNING); // Oneshot processes go into the disabled state on exit, // except when manually restarted. // 對於攜帶了SVC_ONESHOT而且未攜帶SVC_RESTART的srvc,將這類服務的標誌置爲SVC_DISABLED,再也不自啓動 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(); // 未攜帶SVC_RESTART的關鍵服務,在規定的間隔內,crash字數過多時,會致使整機重啓; if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) { if (now < time_crashed_ + 4min) { if (++crash_count_ > 4) { LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes"; panic(); } } else { time_crashed_ = now; crash_count_ = 1; } } // 將待重啓srvc的標誌位置爲SVC_RESTARTING(init進程將根據該標誌位,重啓服務) flags_ &= (~SVC_RESTART); flags_ |= SVC_RESTARTING; // Execute all onrestart commands for this service. // 重啓在init.rc文件中帶有onrestart選項的服務 onrestart_.ExecuteAllCommands(); NotifyStateChange("restarting"); return; }
不難看出,Reap函數的主要做用就是清除問題進程相關的資源,而後根據進程對應的類型,決定是否重啓機器或重啓進程。
咱們在這一部分的最後,看看定義於system/core/init/Action.cpp中的ExecuteAllCommands函數:
void Action::ExecuteAllCommands() const { for (const auto& c : commands_) { ExecuteCommand(c); } } void Action::ExecuteCommand(const Command& command) const { android::base::Timer t; // 進程重啓時,將執行對應的函數 int result = command.InvokeFunc(); // 打印log auto duration = t.duration(); // Any action longer than 50ms will be warned to user as slow operation if (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() << ") returned " << result << " took " << duration.count() << "ms."; } }
整個signal_handler_init的內容比較多,在此總結一下:signal_handler_init的本質就是監聽子進程死亡的信息,而後進行對應的清理工做,並根據死亡進程的類型,決定是否須要重啓進程或機器。
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ /* 02. 清空環境變量 */ /* 03. 完成SELinux相關工做 */ /* 04. 建立epoll句柄 */ /* 05. 裝載子進程信號處理器 */ property_load_boot_defaults(); // 進程調用property_load_boot_defaults進行默認屬性配置相關的工做 export_oem_lock_status(); // 最終就是決定"ro.boot.flash.locked"的值 start_property_service(); // 啓動屬性服務 set_usb_controller(); ... ... }
老樣子,這邊咱們跟蹤幾個重要的函數。
void property_load_boot_defaults() { // 就是從各類路徑讀取默認配置 // load_properties_from_file的基本操做就是read_file,而後解析並設置 if (!load_properties_from_file("/system/etc/prop.default", NULL)) { // Try recovery path if (!load_properties_from_file("/prop.default", NULL)) { // Try legacy path load_properties_from_file("/default.prop", NULL); } } load_properties_from_file("/odm/default.prop", NULL); load_properties_from_file("/vendor/default.prop", NULL); update_sys_usb_config(); // 就是設置"persist.sys.usb.config"相關的配置 }
如代碼所示,property_load_boot_defaults 實際上就是調用 load_properties_from_file 解析配置文件,而後根據解析的結果,設置系統屬性。
void start_property_service() { property_set("ro.property_service.version", "2"); // 建立了一個非阻塞socket property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, false, 0666, 0, 0, nullptr, sehandle); if (property_set_fd == -1) { PLOG(ERROR) << "start_property_service socket creation failed"; exit(1); } // 調用listen函數監聽property_set_fd, 因而該socket變成一個server listen(property_set_fd, 8); // 監聽server socket上是否有數據到來 register_epoll_handler(property_set_fd, handle_property_set_fd); }
init進程在共享內存區域中,建立並初始化屬性域。其它進程能夠訪問屬性域中的值,但更改屬性值僅能在init進程中進行。這就是init進程調用start_property_service的緣由。
其它進程修改屬性值時,要預先向init進程提交值變動申請,而後init進程處理該申請,並修改屬性值。在訪問和修改屬性時,init進程均可以進行權限控制。
/* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ /* 02. 清空環境變量 */ /* 03. 完成SELinux相關工做 */ /* 04. 建立epoll句柄 */ /* 05. 裝載子進程信號處理器 */ /* 06. 啓動屬性服務*/ const BuiltinFunctionMap function_map; // system/core/init/builtins.cpp,定義Action中的function_map_爲BuiltinFuntionMap Action::set_function_map(&function_map); // 在Action中保存function_map對象,記錄了命令與函數之間的對應關係 /* 07. 匹配命令和函數之間對應關係 */ /* ------------ 第二階段 ------------ END ------------ */ ... ... }
至此,init進程的準備工做執行完畢, 接下來就要開始解析init.rc文件了。
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ /* 02. 清空環境變量 */ /* 03. 完成SELinux相關工做 */ /* 04. 建立epoll句柄 */ /* 05. 裝載子進程信號處理器 */ /* 06. 啓動屬性服務*/ /* 07. 匹配命令和函數之間對應關係 */ /* ------------ 第二階段 ------------ END ------------ */ /* ------------ 第三階段 ----------- BEGIN------------ */ ActionManager& am = ActionManager::GetInstance(); ServiceManager& sm = ServiceManager::GetInstance(); Parser& parser = Parser::GetInstance(); // 構造解析文件用的parser對象 // 爲一些類型的關鍵字,建立特定的parser parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm)); // 增長ServiceParser爲一個section,對應name爲service parser.AddSectionParser("on", std::make_unique<ActionParser>(&am)); // 增長ActionParser爲一個section,對應name爲action parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser)); // 增長ActionParser爲一個section,對應name爲import std::string bootscript = GetProperty("ro.boot.init_rc", ""); // 判斷是否存在bootscript // 若是沒有bootscript,則解析init.rc文件 if (bootscript.empty()) { // 8.0引入 parser.ParseConfig("/init.rc"); // 開始實際的解析過程 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 { // 若存在bootscript, 則解析bootscript 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); } ... ... }
若是沒有定義bootScript,那麼init進程仍是會解析init.rc文件。init.rc文件是在init進程啓動後執行的啓動腳本,文件中記錄着init進程需執行的操做。
此處解析函數傳入的參數爲「/init.rc」,解析的是運行時與init進程同在根目錄下的init.rc文件。該文件在編譯前,定義於system/core/rootdir/init.rc中。
✨ 繼續往下分析main函數以前;
✨ 咱們先了解一下init.rc是什麼,而後分析下parser解析init.rc過程;
✨ 最後咱們再繼續跟源碼!
init.rc是一個配置文件,內部由Android初始化語言編寫(Android Init Language)編寫的腳本,主要包含五種類型語句:Action、Command、Service、Option 和 Import,在分析代碼的過程當中咱們會詳細介紹。
init.rc的配置代碼在:system/core/rootdir/init.rc 中。
init.rc文件是在init進程啓動後執行的啓動腳本,文件中記錄着init進程需執行的操做。
init.rc文件大體分爲兩大部分:
一部分是以「on」關鍵字開頭的 動做列表(action list):
on early-init // Action類型語句 # Set init and its forked children's oom_adj. // #:註釋符號 write /proc/1/oom_score_adj -1000 ... ... # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628. mkdir /mnt 0775 root system ... ... start ueventd
Action類型語句格式:
on <trigger> [&& <trigger>]* // 設置觸發器 <command> <command> // 動做觸發以後要執行的命令
另外一部分是以「service」關鍵字開頭的 服務列表(service list): 如 Zygote
service ueventd /sbin/ueventd // Service類型語句 class core critical seclabel u:r:ueventd:s0
Service類型語句格式:
service <name> <pathname> [ <argument> ]* // <service的名字><執行程序路徑><傳遞參數> <option> // option是service的修飾詞,影響何時、如何啓動services <option> ...
藉助系統環境變量或Linux命令,
🏹 動做列表用於建立所需目錄,以及爲某些特定文件指定權限;
🏹 服務列表用來記錄init進程須要啓動的一些子進程,如上面代碼所示,service關鍵字後的第一個字符串表示服務(子進程)的名稱,第二個字符串表示服務的執行路徑。
值得一提的是從Android 7.0後的源碼,對init.rc文件進行了拆分,每一個服務一個rc文件。咱們要分析的zygote服務的啓動腳本則在init.zygoteXX.rc中定義。
在init.rc的import段咱們看到以下代碼:
import /init.${ro.zygote}.rc // 能夠看出init.rc再也不直接引入一個固定的文件,而是根據屬性ro.zygote的內容來引入不一樣的文件
從android5.0開始,android開始支持64位的編譯,zygote自己也就有了32位和64位的區別,因此在這裏用ro.zygote屬性來控制啓動不一樣版本的zygote進程。
init.rc位於/system/core/rootdir下。在這個路徑下還包括四個關於zygote的rc文件。分別是init.zygote32.rc,init.zygote32_64.rc,init.zygote64.rc,init.zygote64_32.rc,由硬件決定調用哪一個文件。
這裏拿32位處理器爲例,init.zygote32.rc的代碼以下所示:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main # class是一個option,指定zygote服務的類型爲main priority -20 user root group root readproc socket zygote stream 660 root system # socket關鍵字表示一個option,建立一個名爲dev/socket/zygote,類型爲stream,權限爲660的socket onrestart write /sys/android_power/request_state wake # onrestart是一個option,說明在zygote重啓時須要執行的command 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 zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server 語句解讀:
在Init.zygote32.rc中,定義了一個zygote服務:zygote,由關鍵字service告訴init進程建立一個名爲zygote的進程,這個進程要執行的程序是:/system/bin/app_process,給這個進程四個參數:
🏹 -Xzygote:該參數將做爲虛擬機啓動時所需的參數
🏹 /system/bin:表明虛擬機程序所在目錄
🏹 --zygote:指明以ZygoteInit.java類中的main函數做爲虛擬機執行入口
🏹 --start-system-server:告訴Zygote進程啓動systemServer進程
回顧解析init.rc的代碼:
int main(int argc, char** argv) { ... ... ActionManager& am = ActionManager::GetInstance(); ServiceManager& sm = ServiceManager::GetInstance(); Parser& parser = Parser::GetInstance(); // 構造解析文件用的parser對象 parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm)); // 增長ServiceParser爲一個section,對應name爲service parser.AddSectionParser("on", std::make_unique<ActionParser>(&am)); // 增長ActionParser爲一個section,對應name爲action parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser)); // 增長ActionParser爲一個section,對應name爲import std::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) { parser.ParseConfig("/init.rc"); // 開始實際的解析過程 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); } ... ... }
咱們發如今解析前,使用了Parser類(在init目錄下的 init_parser.h 中定義),構造了parser對象:
Parser& parser = Parser::GetInstance(); // 構造解析文件用的parser對象
初始化ServiceParser用來解析 「service」塊,ActionParser用來解析"on"塊,ImportParser用來解析「import」塊,「import」是用來引入一個init配置文件,來擴展當前配置的。
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm)); // 增長ServiceParser爲一個section,對應name爲service parser.AddSectionParser("on", std::make_unique<ActionParser>(&am)); // 增長ActionParser爲一個section,對應name爲action parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser)); // 增長ActionParser爲一個section,對應name爲import
/system/core/init/readme.txt中對init文件中的全部關鍵字作了介紹,主要包含了Actions, Commands, Services, Options, and Imports等,可自行學習解讀。
下面就是分析解析過程了:parser.ParseConfig("/init.rc") (函數定義於 system/core/init/init_parser.cpp 中)
bool Parser::ParseConfig(const std::string& path) { if (is_dir(path.c_str())) { // 判斷傳入參數是否爲目錄地址 return ParseConfigDir(path); // 遞歸目錄,最終仍是靠ParseConfigFile來解析實際的文件 } return ParseConfigFile(path); // 傳入參數爲文件地址 }
先來看看ParseConfigDir函數:
bool Parser::ParseConfigDir(const std::string& path) { LOG(INFO) << "Parsing directory " << path << "..."; std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir); if (!config_dir) { PLOG(ERROR) << "Could not import directory '" << path << "'"; return false; } // 遞歸目錄,獲得須要處理的文件 dirent* current_file; std::vector<std::string> files; while ((current_file = readdir(config_dir.get()))) { // Ignore directories and only process regular files. if (current_file->d_type == DT_REG) { std::string current_path = android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name); files.emplace_back(current_path); } } // Sort first so we load files in a consistent order (bug 31996208) std::sort(files.begin(), files.end()); for (const auto& file : files) { // 容易看出,最終還是調用ParseConfigFile if (!ParseConfigFile(file)) { LOG(ERROR) << "could not import file '" << file << "'"; } } return true; }
接下來就重點分析ParseConfigFile():
bool Parser::ParseConfigFile(const std::string& path) { ... ... android::base::Timer t; std::string data; std::string err; if (!ReadFile(path, &data, &err)) { // 讀取路徑指定文件中的內容,保存爲字符串形式 LOG(ERROR) << err; return false; } ... ... ParseData(path, data); // 解析獲取的字符串 ... ... return true; }
ParseConfigFile只是讀取文件的內容並轉換爲字符串,實際的解析工做被交付給ParseData。
ParseData函數定義於system/core/init/init_parser.cpp中,負責根據關鍵字解析出服務和動做。動做與服務會以鏈表節點的形式註冊到service_list與action_list中,service_list與action_list是init進程中聲明的全局結構體。
跟蹤ParseData():
void Parser::ParseData(const std::string& filename, const std::string& data) { //TODO: Use a parser with const input and remove this copy // copy一波數據 std::vector<char> data_copy(data.begin(), data.end()); data_copy.push_back('\0'); // 解析用的結構體 parse_state state; state.line = 0; state.ptr = &data_copy[0]; state.nexttoken = 0; SectionParser* section_parser = nullptr; std::vector<std::string> args; for (;;) { switch (next_token(&state)) { // next_token以行爲單位分割參數傳遞過來的字符串,初始沒有分割符時,最早走到T_TEXT分支 case T_EOF: if (section_parser) { section_parser->EndSection(); // 解析結束 } return; case T_NEWLINE: state.line++; if (args.empty()) { break; } ... ... // 在前文建立parser時,咱們爲service,on,import定義了對應的parser // 這裏就是根據第一個參數,判斷是否有對應的parser if (section_parsers_.count(args[0])) { if (section_parser) { // 結束上一個parser的工做,將構造出的對象加入到對應的service_list與action_list中 section_parser->EndSection(); } // 獲取參數對應的parser section_parser = section_parsers_[args[0]].get(); std::string ret_err; // 調用實際parser的ParseSection函數 if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) { LOG(ERROR) << filename << ": " << state.line << ": " << ret_err; section_parser = nullptr; } } else if (section_parser) { std::string ret_err; /* * 若是第一個參數不是service,on,import * 則調用前一個parser的ParseLineSection函數 * 這裏至關於解析一個參數塊的子項 */ if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) { LOG(ERROR) << filename << ": " << state.line << ": " << ret_err; } } // 清空本次解析的數據 args.clear(); break; case T_TEXT: // 將本次解析的內容寫入到args中 args.emplace_back(state.text); break; } } }
上面的代碼看起來比較複雜,但實際上就是面向對象,根據不一樣的關鍵字,使用不一樣的parser對象進行解析。
至此,init.rc解析完!Ok,別忘了,main函數尚未分析完,繼續往下看。
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ /* 02. 清空環境變量 */ /* 03. 完成SELinux相關工做 */ /* 04. 建立epoll句柄 */ /* 05. 裝載子進程信號處理器 */ /* 06. 啓動屬性服務*/ /* 07. 匹配命令和函數之間對應關係 */ /* ------------ 第二階段 ------------ END ------------ */ /* ------------ 第三階段 ----------- BEGIN------------ */ /* init解析 */ /* ------------ 第三階段 ----------- END ------------ */ /* ------------ 第四階段 ----------- BEGIN------------ */ // 經過am對命令執行順序進行控制 // ActionManager& am = ActionManager::GetInstance(); // init執行命令觸發器主要分爲early-init,init,late-init,boot等 am.QueueEventTrigger("early-init"); // 添加觸發器early-init,執行on 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"); // 添加觸發器init,執行on init內容,主要包括建立/掛在一些目錄,以及symlink等 // 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"); // on charger階段 } else { am.QueueEventTrigger("late-init"); // 非充電模式添加觸發器last-init } // Run all property triggers based on current state of the properties. am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers"); }
繼續分析main函數:
int main(int argc, char** argv) { /* ------------ 第一階段 ------------ BEGIN------------ */ /* 01. 判斷及增長環境變量 */ /* 02. 建立文件系統目錄並掛載相關的文件系統 */ /* 03. 重定向輸入輸出/內核Log系統 */ /* 04. 掛在一些分區設備 */ /* 05. 完成SELinux相關工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一階段 ------------- END ------------ */ /* ------------ 第二階段 ------------ BEGIN------------ */ /* 01. 初始化屬性域 */ /* 02. 清空環境變量 */ /* 03. 完成SELinux相關工做 */ /* 04. 建立epoll句柄 */ /* 05. 裝載子進程信號處理器 */ /* 06. 啓動屬性服務*/ /* 07. 匹配命令和函數之間對應關係 */ /* ------------ 第二階段 ------------ END ------------ */ /* ------------ 第三階段 ----------- BEGIN------------ */ /* init解析 */ /* ------------ 第三階段 ----------- END ------------ */ /* ------------ 第四階段 ----------- BEGIN------------ */ /* 01. 向執行隊列中添加其餘action */ /* 02. 其他工做 */ 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())) { am.ExecuteOneCommand(); // 依次執行每一個action中攜帶command對應的執行函數 } if (!(waiting_for_prop || sm.IsWaitingForExec())) { if (!shutting_down) restart_processes(); // 重啓一些掛掉的進程 // 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; // 有action待處理,不等待 } epoll_event ev; // 沒有事件到來的話,最多阻塞timeout時間 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) { //有事件到來,執行對應處理函數 //根據上文知道,epoll句柄(即epoll_fd)主要監聽子進程結束,及其它進程設置系統屬性的請求 ((void (*)()) ev.data.ptr)(); } } } // end main
至此,Init.cpp的main函數分析完畢!init進程已經啓動完成,一些重要的服務如core服務和main服務也都啓動起來,並啓動了zygote(/system/bin/app_process64)進程,zygote初始化時會建立虛擬機,啓動systemserver等。
01. http://www.javashuo.com/article/p-vvhokgra-et.html
02. https://blog.csdn.net/gaugamela/article/details/79280385
03. http://qiangbo.space/2016-10-10/AndroidAnatomy_Process_Creation/