Android6.0源码分析—— Zygote进程分析(补充)
来源:互联网 发布:外汇k线图软件 编辑:程序博客网 时间:2024/04/27 19:04
此博文为《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进程分析(补充)
- Android6.0源码分析—— Zygote进程分析(补充)
- android6.0源码分析之Zygote进程分析
- android6.0源码分析之Zygote进程分析
- Android5.0源码分析—— Zygote进程分析
- Android5.0源码分析—— Zygote进程分析
- Android Zygote进程源码分析
- Zygote进程源码分析之一
- Android Zygote进程源码分析
- Android6.0的phone应用源码分析(2)——phone相关进程启动分析
- Android6.0的phone应用源码分析(2)——phone相关进程启动分析
- Android源码分析--Zygote进程分析
- Android6.0 Zygote进程
- 源码分析 --- 系统进程Zygote启动过程
- Zygote进程源码分析之二
- Zygote进程启动过程源码分析
- Android Zygote源码分析
- Android6.0的phone应用源码分析(3)——phone 拨号UI分析
- ACM-CCPC中国大学生程序设计竞赛长春赛区(2016)地区赛——花开花落两相知
- Android6.0的SMS(短信)源码分析--短信发送
- Android6.0的SMS(短信)源码分析--短信接收
- NLTK学习总结(一)
- Android5.0源码分析—— Zygote进程分析
- Android6.0源码分析—— Zygote进程分析(补充)
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- 基于OPENCV的字符表情的制作
- 小水怪微信爬虫(7)
- Android 异步消息处理机制(Handler 、 Looper 、MessageQueue)源码解析
- Android异步任务机制之AsycTask
- SCP 命令
- Android background processing with Handlers, AsyncTask and Loaders - Tutorial
- Web前端工程入门简介