Android Pie源码阅读 -----深入理解init(一)
?
在閱讀源碼之前,我們應(yīng)要熟悉整個(gè)Android的系統(tǒng)架構(gòu),在針對(duì)某一層進(jìn)行深入理解,否則東看西看沒(méi)有方向沒(méi)有目標(biāo)會(huì)導(dǎo)致思緒紊亂,這樣讀源碼的效果不佳
這篇文章主要參考gityuan閱讀Android 7.0的源碼,順著他的思路我理了一遍Android 9.0的代碼,如果有地方理解錯(cuò)了,謝謝指出!!
?Android整體系統(tǒng)框架
?
?Linux 內(nèi)核
Android 平臺(tái)的基礎(chǔ)是 Linux 內(nèi)核。這一層包含Linux內(nèi)核和一些驅(qū)動(dòng)模塊(比如USB驅(qū)動(dòng)、Camera驅(qū)動(dòng)等)
硬件抽象層 (HAL)
硬件抽象層 (HAL) 提供標(biāo)準(zhǔn)界面,向更高級(jí)別的 Java API 框架顯示設(shè)備硬件功能。HAL 包含多個(gè)庫(kù)模塊,其中每個(gè)模塊都為特定類型的硬件組件實(shí)現(xiàn)一個(gè)界面,例如相機(jī)或藍(lán)牙模塊。當(dāng)框架 API 要求訪問(wèn)設(shè)備硬件時(shí),Android 系統(tǒng)將為該硬件組件加載庫(kù)模塊。
Android Runtime
對(duì)于運(yùn)行 Android 5.0(API 級(jí)別 21)或更高版本的設(shè)備,每個(gè)應(yīng)用都在其自己的進(jìn)程中運(yùn)行,并且有其自己的 Android Runtime (ART) 實(shí)例。ART 編寫為通過(guò)執(zhí)行 DEX 文件在低內(nèi)存設(shè)備上運(yùn)行多個(gè)虛擬機(jī),DEX 文件是一種專為 Android 設(shè)計(jì)的字節(jié)碼格式,經(jīng)過(guò)優(yōu)化,使用的內(nèi)存很少。編譯工具鏈(例如 Jack)將 Java 源代碼編譯為 DEX 字節(jié)碼,使其可在 Android 平臺(tái)上運(yùn)行。
ART 的部分主要功能包括:
預(yù)先 (AOT) 和即時(shí) (JIT) 編譯
優(yōu)化的垃圾回收 (GC)
更好的調(diào)試支持,包括專用采樣分析器、詳細(xì)的診斷異常和崩潰報(bào)告,并且能夠設(shè)置監(jiān)視點(diǎn)以監(jiān)控特定字段
在 Android 版本 5.0(API 級(jí)別 21)之前,Dalvik 是 Android Runtime。如果您的應(yīng)用在 ART 上運(yùn)行效果很好,那么它應(yīng)該也可在 Dalvik 上運(yùn)行,但反過(guò)來(lái)不一定。
?原生 C/C++ 庫(kù)?
許多核心 Android 系統(tǒng)組件和服務(wù)(例如 ART 和 HAL)構(gòu)建自原生代碼,需要以 C 和 C++ 編寫的原生庫(kù)。Android 平臺(tái)提供 Java 框架 API 以向應(yīng)用顯示其中部分原生庫(kù)的功能。例如,您可以通過(guò) Android 框架的 Java OpenGL API 訪問(wèn) OpenGL ES,以支持在應(yīng)用中繪制和操作 2D 和 3D 圖形。
Java API 框架
您可通過(guò)以 Java 語(yǔ)言編寫的 API 使用 Android OS 的整個(gè)功能集。這些 API 形成創(chuàng)建 Android 應(yīng)用所需的構(gòu)建塊,它們可簡(jiǎn)化核心模塊化系統(tǒng)組件和服務(wù)的重復(fù)使用,這一點(diǎn)也是我們常用到的一塊地方
系統(tǒng)應(yīng)用?
Android 隨附一套用于電子郵件、短信、日歷、互聯(lián)網(wǎng)瀏覽和聯(lián)系人等的核心應(yīng)用。平臺(tái)隨附的應(yīng)用與用戶可以選擇安裝的應(yīng)用一樣,沒(méi)有特殊狀態(tài)。因此第三方應(yīng)用可成為用戶的默認(rèn)網(wǎng)絡(luò)瀏覽器、短信 Messenger 甚至默認(rèn)鍵盤(有一些例外,例如系統(tǒng)的“設(shè)置”應(yīng)用)。
**所以我們這次就先從Native C/C++這一邊開始看,這里面有許多核心的Android系統(tǒng)組件和服務(wù),接下來(lái)我們先下載源碼 platform-system-core-pie-release,從這邊開始就進(jìn)入Native源碼的世界了**
?
這個(gè)源碼里的官方提供的文檔有這么一句話:
/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這個(gè)函數(shù)負(fù)責(zé)系統(tǒng)的初始化,所以接下來(lái)也會(huì)從這個(gè)點(diǎn)切入。
?
? ?1.init概述
? init是Android系統(tǒng)中用戶空間的第一個(gè)進(jìn)程,它的進(jìn)程號(hào)是1。我們肯能經(jīng)常聽到過(guò)zygote,那么這個(gè)zygote是如何被創(chuàng)建的呢,init的屬性服務(wù)又是如何提供的。
??
? ? ?2.init分析
以下代碼很長(zhǎng),請(qǐng)保持耐心!
在下面代碼我會(huì)將一些函數(shù)標(biāo)記為log n ,大家可以通過(guò)log1等去快速查找相應(yīng)函數(shù)所代表的含義
?2.1main函數(shù)
首先看入口函數(shù)main的代碼
?
int main(int argc, char** argv) {...(將文件屬性設(shè)置為077權(quán)限)// 第一階段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()函數(shù):設(shè)置建立新文件時(shí)的權(quán)限遮罩clearenv(); ?//清除所有的環(huán)境變量setenv("PATH", _PATH_DEFPATH, 1); //設(shè)置環(huán)境變量// 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//以上掛載一些文件(掛載:它指將一個(gè)設(shè)備(通常是存儲(chǔ)設(shè)備)掛接到一個(gè)已存在的目錄上。)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";}//此時(shí)我們處于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.//設(shè)置所有進(jìn)程都可以訪問(wèn)的會(huì)話密匙環(huán)。它將保存FBE加密密鑰之類的東西。任何進(jìn)程都不應(yīng)覆蓋其會(huì)話密鑰環(huán)。keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);// Indicate that booting is in progress to background fw loaders, etc.//指示正在引導(dǎo)到后臺(tái)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.如果參數(shù)同時(shí)在命令行和DT中傳遞,// DT中設(shè)置的屬性總是優(yōu)先于命令行屬性。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)核變量傳播到內(nèi)部變量//用于init以及當(dāng)前所需的屬性。export_kernel_boot_props();// Make the time that init started available for bootstat to log.//讓init啟動(dòng)的時(shí)間可供bootstat進(jìn)行日志記錄。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版本設(shè)置為只有框架的OTA匹配。unsetenv("INIT_SECOND_STAGE");unsetenv("INIT_STARTED_AT");unsetenv("INIT_SELINUX_TOOK");unsetenv("INIT_AVB_VERSION");// Now set up SELinux for second stage.//現(xiàn)在為第二階段設(shè)置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沒(méi)有CAP_SYS_BOOT功能,它將在容器中運(yùn)行。 //在這種情況下,接收SIGTERM將導(dǎo)致系統(tǒng)關(guān)閉。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...//隊(duì)列一個(gè)等待冷啟動(dòng)完成的動(dòng)作,這樣我們就知道ueventd已經(jīng)設(shè)置了所有/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./// /……這樣我們就可以開始對(duì)需要/dev中的內(nèi)容的操作進(jìn)行排隊(duì)。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.//觸發(fā)所有啟動(dòng)操作,讓我們開始。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.//不要在充電器模式下安裝文件系統(tǒng)或啟動(dòng)核心系統(tǒng)服務(wù)。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.//根據(jù)屬性的當(dāng)前狀態(tài)運(yùn)行所有屬性觸發(fā)器。am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");while (true) {// By default, sleep until something happens.//默認(rèn)情況下,睡眠直到一些事情觸發(fā)。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();//循環(huán)遍歷事件隊(duì)列,直到有一個(gè)操作要執(zhí)行}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.//如果有一個(gè)過(guò)程需要重新啟動(dòng),及時(shí)醒來(lái)。?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;// 循環(huán)等待事件發(fā)生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系統(tǒng)
??
? includer log.h
??
??
?2.3 log3:QueueBuiltinAction ?對(duì)內(nèi)容的一系列操作
?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重點(diǎn)分析
?
接下來(lái),解讀init的main方法中的4大塊核心知識(shí)點(diǎn):信號(hào)處理、rc文件語(yǔ)法、啟動(dòng)服務(wù)以及屬性服務(wù)
?
?3.1信號(hào)處理
? sigchld_handler_init();
? 首先看下這個(gè)函數(shù)做了什么事
??
? [-> sigchld_handler.cpp]
??
?
??
? 每個(gè)進(jìn)程在處理其他進(jìn)程發(fā)送的signal信號(hào)時(shí)都需要先注冊(cè),當(dāng)進(jìn)程的運(yùn)行狀態(tài)改變或終止時(shí)會(huì)產(chǎn)生某種signal信號(hào),init進(jìn)程是所有用戶空間進(jìn)程的父進(jìn)程,當(dāng)其子進(jìn)程終止時(shí)產(chǎn)生sigchld信號(hào),傳遞參數(shù)給sigaction結(jié)構(gòu)體,便完成信號(hào)處理的過(guò)程。
? 這里看下2個(gè)重要函數(shù):
??
?
?
??
我們繼續(xù)看下 ReapAnyOutstandingChildren()函數(shù),里面的ReapOneProcess()又是一個(gè)很長(zhǎng)的函數(shù)
? 這篇還未完成,等之后在完善下,給各位帶來(lái)不便。
??
?
總結(jié)
以上是生活随笔為你收集整理的Android Pie源码阅读 -----深入理解init(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: HDU 6078Wavel Sequen
- 下一篇: element-ui message 显