Android6.0源码分析—— Zygote进程分析(补充)
此博文為《Android5.0源碼分析—— Zygote進程分析》的補充
我們已經知道Android?5.0已經默認了ART,今天本想回去查看一下這個部分,于是回到init進程中去尋找源碼,發現6.0的Zygote部分也小有變動,因此更新一下。
首先是init.c變成了init.cpp,這其實也就意味著在init中增加了類的概念。但是仔細查看init.h發現并沒有class關鍵字。只有很多的struct。如果對C++比較了解,就應該知道C++的struct和C中的struct其實已經是不一樣的概念了,在C++中struct除了公私與class對調外,其他的基本上沒有區別。在init.h中定義了以下結構體:
| struct service { ????void NotifyStateChange(const char* new_state); ? ??????? /* list of all services */ ??? struct listnode slist; ? ??? char *name; ??? const char *classname; ? ??? unsigned flags; ??? pid_t pid; ??? time_t time_started;??? /* time of last start */ ??? time_t time_crashed;??? /* first crash within inspection window */ ??? int nr_crashed;???????? /* number of times crashed within window */ ? ??? uid_t uid; ??? gid_t gid; ??? gid_t supp_gids[NR_SVC_SUPP_GIDS]; ? ??size_t nr_supp_gids; ? ??? const char* seclabel; ? ??? struct socketinfo *sockets; ??? struct svcenvinfo *envvars; ? ??? struct action onrestart;? /* Actions to execute on restart. */ ? ??? std::vector<std::string>* writepid_files_; ? ??? /* keycodes for triggering this service via /dev/keychord */ ??? int *keycodes; ??? int nkeycodes; ??? int keychord_id; ? ??? IoSchedClass ioprio_class; ??? int ioprio_pri; ? ??? int nargs; ????/* "MUST BE AT THE END OF THE STRUCT" */ ??? char *args[1]; }; |
可以看到,與之前5.0版本的最大區別就是結構體內多了一個函數(5.0也有類似功能的函數但是被放在結構體外)!從函數的起名來看這個函數應該是負責通知Service的狀態變化。Android6.0作這樣的改變我認為僅僅是為了封裝。
另外一個就是init.main作了比較大的調整,但是同樣這些調整也只是讓整個程序的封裝性和可讀性更強罷了。實質的處理流程并沒有什么變化。調整后的main函數如下:(變動比較大的部分已經用金底紅字標出)
| int main(int argc, char** argv) { ??? if (!strcmp(basename(argv[0]), "ueventd")) { ??????? return ueventd_main(argc, argv); ??? } ? ??? if (!strcmp(basename(argv[0]), "watchdogd")) { ??????? return watchdogd_main(argc, argv); ??? } ? ????// Clear the umask. ??? umask(0); ? ??? add_environment("PATH", _PATH_DEFPATH); ? ??? bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0); ? ????// 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. ??? if (is_first_stage) { ??????? mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); ??????? mkdir("/dev/pts", 0755); ??????? mkdir("/dev/socket", 0755); ??????? mount("devpts", "/dev/pts", "devpts", 0, NULL); ??????? mount("proc", "/proc", "proc", 0, NULL); ??????? mount("sysfs", "/sys", "sysfs", 0, NULL); ??? } ? ????// We must have some place other than / to create the device nodes for ??? // kmsg and null, otherwise we won't be able to remount / read-only ??? // later on. Now that tmpfs is mounted on /dev, we can actually talk ??? // to the outside world. ??? open_devnull_stdio(); ??? klog_init(); ??? klog_set_level(KLOG_NOTICE_LEVEL); ? ??? NOTICE("init%s started!\n", is_first_stage ? "" : " second stage"); ? ??? if (!is_first_stage) { ????????// Indicate that booting is in progress to background fw loaders, etc. ??????? 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. ??????? process_kernel_dt(); ??????? process_kernel_cmdline(); ? ????????// Propogate the kernel variables to internal variables ??????? // used by init as well as the current required properties. ??????? export_kernel_boot_props(); ??? } ? ????// Set up SELinux, including loading the SELinux policy if we're in the kernel domain. ??? selinux_initialize(is_first_stage); ? ????// If we're in the kernel domain, re-exec init to transition to the init domain now ??? // that the SELinux policy has been loaded. ??? if (is_first_stage) { ??????? if (restorecon("/init") == -1) { ??????????? ERROR("restorecon failed: %s\n", strerror(errno)); ??????????? security_failure(); ??????? } ??????? char* path = argv[0]; ??????? char* args[] = { path, const_cast<char*>("--second-stage"), nullptr }; ??????? if (execv(path, args) == -1) { ??????????? ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno)); ??????????? security_failure(); ??????? } ??? } ? ????// These directories were necessarily created before initial policy load ??? // and therefore need their security context restored to the proper value. ??? // This must happen before /dev is populated by ueventd. ??? INFO("Running restorecon...\n"); ??? restorecon("/dev"); ??? restorecon("/dev/socket"); ??? restorecon("/dev/__properties__"); ??? restorecon_recursive("/sys"); ? ????epoll_fd = epoll_create1(EPOLL_CLOEXEC); ??? if (epoll_fd == -1) { ??????? ERROR("epoll_create1 failed: %s\n", strerror(errno)); ??????? exit(1); ??? } ? ??? signal_handler_init(); ? ??? property_load_boot_defaults(); ????start_property_service(); ? ????init_parse_config_file("/init.rc"); ? ??? action_for_each_trigger("early-init", action_add_queue_tail); ? ????// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev... ??? queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); ????// ... so that we can start queuing up actions that require stuff from /dev. ??? queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); ??? queue_builtin_action(keychord_init_action, "keychord_init"); ??? queue_builtin_action(console_init_action, "console_init"); ? ????// Trigger all the boot actions to get us started. ??? action_for_each_trigger("init", action_add_queue_tail); ? ????// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random ??? // wasn't ready immediately after wait_for_coldboot_done ??? queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); ? ??? // Don't mount filesystems or start core system services in charger mode. ??? char bootmode[PROP_VALUE_MAX]; ??? if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) { ??????? action_for_each_trigger("charger", action_add_queue_tail); ??? } else { ??????? action_for_each_trigger("late-init", action_add_queue_tail); ??? } ? ????// Run all property triggers based on current state of the properties. ??? queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); ? ??? while (true) { ??????? if (!waiting_for_exec) { ????????????execute_one_command(); ????????????restart_processes(); ??????? } ? ??????? int timeout = -1; ??????? if (process_needs_restart) { ??????????? timeout = (process_needs_restart - gettime()) * 1000; ??????????? if (timeout < 0) ??????????????? timeout = 0; ??????? } ? ??????? if (!action_queue_empty() || cur_action) { ??????????? timeout = 0; ??????? } ? ??????? bootchart_sample(&timeout); ? ??????? epoll_event ev; ????????int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout)); ??????? if (nr == -1) { ??????????? ERROR("epoll_wait failed: %s\n", strerror(errno)); ??????? } else if (nr == 1) { ????????????((void (*)()) ev.data.ptr)();//其實最根本的改變在這,為了封裝 ??????? } ??? } ? ??? return 0; } |
總的來講,6.0其實就是將原先5.0的方式改成了注冊式的。使得結構性更強。
整理Zygote的啟動大致如下圖所示(帶點紅色的標示C++類,純綠的為java類):
可以看到,ART和Dalvik的啟動是在JniInvocation.init(NULL)中,init的參數可以指定使用哪個虛擬機,如果是NULL則默認ART。實際上可以認為是JniInvocation封裝掉了兩種虛擬機之間的差異(當然這僅僅是說的啟動)。
另一個收獲就是弄清楚了classname啟動,是指一些非Zygote 的java 程序的啟動路徑,如am(shell),這種進程和Zygote孵化出來的進程最大的區別就是沒有binder線程池。
| //App_main.main()中 if (zygote) { ??????? runtime.start("com.android.internal.os.ZygoteInit", args, zygote); ??? } else if (className) { ??????? runtime.start("com.android.internal.os.RuntimeInit", args, zygote); ??? } else { ??????? fprintf(stderr, "Error: no class name or --zygote supplied.\n"); ??????? app_usage(); ??????? LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); ??????? return 10; ??? } |
再次總結Zygote孵化進程的過程如下圖所示。
總結
以上是生活随笔為你收集整理的Android6.0源码分析—— Zygote进程分析(补充)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android5.0源码分析—— Zyg
- 下一篇: Android 异步消息处理机制(Han