Android Pie源码阅读 -----深入理解init(一)
?
在閱讀源碼之前,我們應要熟悉整個Android的系統架構,在針對某一層進行深入理解,否則東看西看沒有方向沒有目標會導致思緒紊亂,這樣讀源碼的效果不佳
這篇文章主要參考gityuan閱讀Android 7.0的源碼,順著他的思路我理了一遍Android 9.0的代碼,如果有地方理解錯了,謝謝指出!!
?Android整體系統框架
?
?Linux 內核
Android 平臺的基礎是 Linux 內核。這一層包含Linux內核和一些驅動模塊(比如USB驅動、Camera驅動等)
硬件抽象層 (HAL)
硬件抽象層 (HAL) 提供標準界面,向更高級別的 Java API 框架顯示設備硬件功能。HAL 包含多個庫模塊,其中每個模塊都為特定類型的硬件組件實現一個界面,例如相機或藍牙模塊。當框架 API 要求訪問設備硬件時,Android 系統將為該硬件組件加載庫模塊。
Android Runtime
對于運行 Android 5.0(API 級別 21)或更高版本的設備,每個應用都在其自己的進程中運行,并且有其自己的 Android Runtime (ART) 實例。ART 編寫為通過執行 DEX 文件在低內存設備上運行多個虛擬機,DEX 文件是一種專為 Android 設計的字節碼格式,經過優化,使用的內存很少。編譯工具鏈(例如 Jack)將 Java 源代碼編譯為 DEX 字節碼,使其可在 Android 平臺上運行。
ART 的部分主要功能包括:
預先 (AOT) 和即時 (JIT) 編譯
優化的垃圾回收 (GC)
更好的調試支持,包括專用采樣分析器、詳細的診斷異常和崩潰報告,并且能夠設置監視點以監控特定字段
在 Android 版本 5.0(API 級別 21)之前,Dalvik 是 Android Runtime。如果您的應用在 ART 上運行效果很好,那么它應該也可在 Dalvik 上運行,但反過來不一定。
?原生 C/C++ 庫?
許多核心 Android 系統組件和服務(例如 ART 和 HAL)構建自原生代碼,需要以 C 和 C++ 編寫的原生庫。Android 平臺提供 Java 框架 API 以向應用顯示其中部分原生庫的功能。例如,您可以通過 Android 框架的 Java OpenGL API 訪問 OpenGL ES,以支持在應用中繪制和操作 2D 和 3D 圖形。
Java API 框架
您可通過以 Java 語言編寫的 API 使用 Android OS 的整個功能集。這些 API 形成創建 Android 應用所需的構建塊,它們可簡化核心模塊化系統組件和服務的重復使用,這一點也是我們常用到的一塊地方
系統應用?
Android 隨附一套用于電子郵件、短信、日歷、互聯網瀏覽和聯系人等的核心應用。平臺隨附的應用與用戶可以選擇安裝的應用一樣,沒有特殊狀態。因此第三方應用可成為用戶的默認網絡瀏覽器、短信 Messenger 甚至默認鍵盤(有一些例外,例如系統的“設置”應用)。
**所以我們這次就先從Native C/C++這一邊開始看,這里面有許多核心的Android系統組件和服務,接下來我們先下載源碼 platform-system-core-pie-release,從這邊開始就進入Native源碼的世界了**
?
這個源碼里的官方提供的文檔有這么一句話:
/init.rc is the primary .rc file and is loaded by the init executable at the beginning of its execution. ?It is responsible for the initial set up of the system.?
init這個函數負責系統的初始化,所以接下來也會從這個點切入。
?
? ?1.init概述
? init是Android系統中用戶空間的第一個進程,它的進程號是1。我們肯能經常聽到過zygote,那么這個zygote是如何被創建的呢,init的屬性服務又是如何提供的。
??
? ? ?2.init分析
以下代碼很長,請保持耐心!
在下面代碼我會將一些函數標記為log n ,大家可以通過log1等去快速查找相應函數所代表的含義
?2.1main函數
首先看入口函數main的代碼
?
int main(int argc, char** argv) {...(將文件屬性設置為077權限)// 第一階段bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);if (is_first_stage) {?boot_clock::time_point start_time = boot_clock::now();// Clear the umask.?umask(0); //umask()函數:設置建立新文件時的權限遮罩clearenv(); ?//清除所有的環境變量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.mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");mkdir("/dev/pts", 0755);mkdir("/dev/socket", 0755);mount("devpts", "/dev/pts", "devpts", 0, NULL);#define MAKE_STR(x) __STRING(x)mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));// Don't expose the raw commandline to unprivileged processes.chmod("/proc/cmdline", 0440);gid_t groups[] = { AID_READPROC };setgroups(arraysize(groups), groups);mount("sysfs", "/sys", "sysfs", 0, NULL);mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));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...InitKernelLogging(argv); // log2//以上掛載一些文件(掛載:它指將一個設備(通常是存儲設備)掛接到一個已存在的目錄上。)LOG(INFO) << "init first stage started!";//log2if (!DoFirstStageMount()) {LOG(FATAL) << "Failed to mount required partitions early ...";}SetInitAvbVersionInRecovery();// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).global_seccomp();// Set up SELinux, loading the SELinux policy.// 安裝SELinuxSelinuxSetupKernelLogging();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.if (selinux_android_restorecon("/init", 0) == -1) {PLOG(FATAL) << "restorecon failed of /init failed";}setenv("INIT_SECOND_STAGE", "true", 1);static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);char* path = argv[0];char* args[] = { path, nullptr };execv(path, args);// execv() only returns if an error happened, in which case we// panic and never fall through this conditional.PLOG(FATAL) << "execv(\"" << path << "\") failed";}//此時我們處于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.//設置所有進程都可以訪問的會話密匙環。它將保存FBE加密密鑰之類的東西。任何進程都不應覆蓋其會話密鑰環。keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);// Indicate that booting is in progress to background fw loaders, etc.//指示正在引導到后臺fw加載器,等等。close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));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中傳遞,// DT中設置的屬性總是優先于命令行屬性。process_kernel_dt();process_kernel_cmdline();// Propagate the kernel variables to internal variables// used by init as well as the current required properties.//將內核變量傳播到內部變量//用于init以及當前所需的屬性。export_kernel_boot_props();// Make the time that init started available for bootstat to log.//讓init啟動的時間可供bootstat進行日志記錄。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.//將libavb版本設置為只有框架的OTA匹配。unsetenv("INIT_SECOND_STAGE");unsetenv("INIT_STARTED_AT");unsetenv("INIT_SELINUX_TOOK");unsetenv("INIT_AVB_VERSION");// Now set up SELinux for second stage.//現在為第二階段設置SELinux。SelinuxSetupKernelLogging();SelabelInitialize();SelinuxRestoreContext();epoll_fd = epoll_create1(EPOLL_CLOEXEC);if (epoll_fd == -1) {PLOG(FATAL) << "epoll_create1 failed";}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.//如果init沒有CAP_SYS_BOOT功能,它將在容器中運行。 //在這種情況下,接收SIGTERM將導致系統關閉。InstallSigtermHandler();}property_load_boot_defaults();export_oem_lock_status();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();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();am.QueueEventTrigger("early-init");// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...//隊列一個等待冷啟動完成的動作,這樣我們就知道ueventd已經設置了所有/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./// /……這樣我們就可以開始對需要/dev中的內容的操作進行排隊。am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");am.QueueBuiltinAction(keychord_init_action, "keychord_init");am.QueueBuiltinAction(console_init_action, "console_init");//log3// 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_doneam.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");// Don't mount filesystems or start core system services in charger mode.//不要在充電器模式下安裝文件系統或啟動核心系統服務。std::string bootmode = GetProperty("ro.bootmode", "");if (bootmode == "charger") {am.QueueEventTrigger("charger");} else {am.QueueEventTrigger("late-init");}// Run all property triggers based on current state of the properties.//根據屬性的當前狀態運行所有屬性觸發器。am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");while (true) {// By default, sleep until something happens.//默認情況下,睡眠直到一些事情觸發。int epoll_timeout_ms = -1;if (do_shutdown && !shutting_down) {do_shutdown = false;if (HandlePowerctlMessage(shutdown_command)) {shutting_down = true;}}if (!(waiting_for_prop || Service::is_exec_service_running())) {am.ExecuteOneCommand();//循環遍歷事件隊列,直到有一個操作要執行}if (!(waiting_for_prop || Service::is_exec_service_running())) {if (!shutting_down) {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)();}}
??
?2.2 log2: InitKernelLogging(argv) Log() log系統
??
? includer log.h
??
??
?2.3 log3:QueueBuiltinAction ?對內容的一系列操作
?am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");am.QueueBuiltinAction(keychord_init_action, "keychord_init");am.QueueBuiltinAction(console_init_action, "console_init");
?3 init重點分析
?
接下來,解讀init的main方法中的4大塊核心知識點:信號處理、rc文件語法、啟動服務以及屬性服務
?
?3.1信號處理
? sigchld_handler_init();
? 首先看下這個函數做了什么事
??
? [-> sigchld_handler.cpp]
??
?
??
? 每個進程在處理其他進程發送的signal信號時都需要先注冊,當進程的運行狀態改變或終止時會產生某種signal信號,init進程是所有用戶空間進程的父進程,當其子進程終止時產生sigchld信號,傳遞參數給sigaction結構體,便完成信號處理的過程。
? 這里看下2個重要函數:
??
?
?
??
我們繼續看下 ReapAnyOutstandingChildren()函數,里面的ReapOneProcess()又是一個很長的函數
? 這篇還未完成,等之后在完善下,給各位帶來不便。
??
?
總結
以上是生活随笔為你收集整理的Android Pie源码阅读 -----深入理解init(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU 6078Wavel Sequen
- 下一篇: 如何在Android TV 桌面添加自定