Android源码下载:https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/
源码编译可参考【牛肉面大神之作】:http://blog.csdn.net/cjpx00008/article/details/60474883
【开篇说明】
在【Android启示录】中,提到了主要的分析对象和分享内容,抛开Android内核级的知识点,学习Android第一步便是“init”,作为天字第一号进程,代码羞涩难懂,但是也极其重要,熟悉init的原理对后面Zygote -- SystemServer -- 核心服务等一些列源码的研究是有很大作用的,所以既然说研究Android源码,就先拿init “庖丁解牛”!
【正文开始】
Init进程,它是一个由内核启动的用户级进程,当Linux内核启动之后,运行的第一个进程是init,这个进程是一个守护进程,确切的说,它是Linux系统中用户控件的第一个进程,所以它的进程号是1。它的生命周期贯穿整个linux 内核运行的始终, linux中所有其它的进程的共同始祖均为init进程,可以通过“adb shell ps | grep init”查看进程号。
Android init进程的入口文件在system/core/init/init.cpp中,由于init是命令行程序,所以分析init.cpp首先应从main函数开始:
Java代码
- 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);
- }
-
-
- umask(0);
- add_environment("PATH", _PATH_DEFPATH);
-
- bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
-
-
- 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);
- #define MAKE_STR(x) __STRING(x)
- mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
- mount("sysfs", "/sys", "sysfs", 0, NULL);
- }
以上代码主要做的工作就是:【创建文件系统目录并挂载相关的文件系统】
Java代码
- int main(int argc, char** argv) {
-
-
-
-
-
-
- open_devnull_stdio();
-
- klog_init();
- klog_set_level(KLOG_NOTICE_LEVEL);
继续分析源码,接下来要做的就是初始化属性域:
Java代码
- int main(int argc, char** argv) {
-
-
-
- NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
- if (!is_first_stage) {
-
- close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
- property_init();
-
-
-
- process_kernel_dt();
- process_kernel_cmdline();
-
-
- export_kernel_boot_props();
- }
看一下property_init方法:位于system/core/init/Property_service.cpp中
Java代码
- void property_init() {
- if (__system_property_area_init()) {
- ERROR("Failed to initialize property area\n");
- exit(1);
- }
- }
继续分析main函数:
Java代码
- int main(int argc, char** argv) {
-
-
-
-
-
- selinux_initialize(is_first_stage);
详细看一下selinux_initialize()函数:
Java代码
- 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) {
- INFO("Loading SELinux policy...\n");
-
- if (selinux_android_load_policy() < 0) {
- ERROR("failed to load policy: %s\n", strerror(errno));
- security_failure();
- }
-
- bool kernel_enforcing = (security_getenforce() == 1);
- bool is_enforcing = selinux_is_enforcing();
- if (kernel_enforcing != is_enforcing) {
-
-
-
- if (security_setenforce(is_enforcing)) {
- ERROR("security_setenforce(%s) failed: %s\n",
- is_enforcing ? "true" : "false", strerror(errno));
- security_failure();
- }
- }
-
- if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
- security_failure();
- }
-
- NOTICE("(Initializing SELinux %s took %.2fs.)\n",
- is_enforcing ? "enforcing" : "non-enforcing", t.duration());
- } else {
- selinux_init_all_handles();
- }
- }
回到main函数中继续分析:
Java代码
- int main(int argc, char** argv) {
-
-
-
-
-
-
-
- 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();
- }
- }
-
-
-
-
- NOTICE("Running restorecon...\n");
- restorecon("/dev");
- restorecon("/dev/socket");
- restorecon("/dev/__properties__");
- restorecon("/property_contexts");
-
- restorecon_recursive("/sys");
-
- epoll_fd = epoll_create1(EPOLL_CLOEXEC);
- if (epoll_fd == -1) {
- ERROR("epoll_create1 failed: %s\n", strerror(errno));
- exit(1);
- }
接着往下分析:
Java代码
- int main(int argc, char** argv) {
-
-
-
- ·
-
-
-
- signal_handler_init();
Note:init是一个守护进程,为了防止init的子进程成为僵尸进程(zombie process),需要init在子进程结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)。
细化signal_handler_init()函数:
Java代码
- void signal_handler_init() {
-
-
- int s[2];
-
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
- ERROR("socketpair failed: %s\n", strerror(errno));
- exit(1);
- }
-
- signal_write_fd = s[0];
- signal_read_fd = s[1];
-
-
- struct sigaction act;
- memset(&act, 0, sizeof(act));
-
- act.sa_handler = SIGCHLD_handler;
- act.sa_flags = SA_NOCLDSTOP;
-
- sigaction(SIGCHLD, &act, 0);
-
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-
- register_epoll_handler(signal_read_fd, handle_signal);
- }
Linux进程通过互相发送接收消息来实现进程间的通信,这些消息被称为“信号”。每个进程在处理其它进程发送的信号时都要注册处理者,处理者被称为信号处理器。
注意到sigaction结构体的sa_flags为SA_NOCLDSTOP。由于系统默认在子进程暂停时也会发送信号SIGCHLD,init需要忽略子进程在暂停时发出的SIGCHLD信号,因此将act.sa_flags 置为SA_NOCLDSTOP,该标志位表示仅当进程终止时才接受SIGCHLD信号。
观察SIGCHLD_handler具体工作:
Java代码
- static void SIGCHLD_handler(int) {
-
- if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
- ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
- }
- }
在装在信号监听器的最后,有如下函数:register_epoll_handler(signal_read_fd, handle_signal);
Java代码
- void register_epoll_handler(int fd, void (*fn)()) {
- epoll_event ev;
- ev.events = EPOLLIN;
- ev.data.ptr = reinterpret_cast<void*>(fn);
-
-
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- ERROR("epoll_ctl failed: %s\n", strerror(errno));
- }
- }
【小结】
当init进程调用signal_handler_init后,一旦收到子进程终止带来的SIGCHLD消息后,将利用信号处理者SIGCHLD_handler向signal_write_fd写入信息; epoll句柄监听到signal_read_fd收消息后,将调用handle_signal进行处理。
查看handle_signal函数:
Java代码
- static void handle_signal() {
-
- char buf[32];
- read(signal_read_fd, buf, sizeof(buf));
-
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
- }
从代码中可以看出,handle_signal只是清空signal_read_fd中的数据,然后调用ServiceManager::GetInstance().ReapAnyOutstandingChildren()。
继续分析:
Java代码
-
- ServiceManager::ServiceManager() {
- }
-
- ServiceManager& ServiceManager::GetInstance() {
- static ServiceManager instance;
- return instance;
- }
- void ServiceManager::ReapAnyOutstandingChildren() {
- while (ReapOneProcess()) {
- }
- }
接下来看下ReapOneProcess这个函数:
Java代码
- bool ServiceManager::ReapOneProcess() {
- int status;
-
-
- pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
- if (pid == 0) {
- return false;
- } else if (pid == -1) {
- ERROR("waitpid failed: %s\n", strerror(errno));
- return false;
- }
-
-
- Service* svc = FindServiceByPid(pid);
-
- std::string name;
- if (svc) {
- name = android::base::StringPrintf("Service '%s' (pid %d)",
- svc->name().c_str(), pid);
- } else {
- name = android::base::StringPrintf("Untracked pid %d", pid);
- }
-
- if (WIFEXITED(status)) {
- NOTICE("%s exited with status %d\n", name.c_str(), WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- NOTICE("%s killed by signal %d\n", name.c_str(), WTERMSIG(status));
- } else if (WIFSTOPPED(status)) {
- NOTICE("%s stopped by signal %d\n", name.c_str(), WSTOPSIG(status));
- } else {
- NOTICE("%s state changed", name.c_str());
- }
-
- if (!svc) {
- return true;
- }
-
- if (svc->Reap()) {
- waiting_for_exec = false;
- RemoveService(*svc);
- }
-
- return true;
- }
继续分析main()函数:
Java代码
- int main(int argc, char** argv) {
-
-
-
- ·
-
-
-
-
- property_load_boot_defaults();
- export_oem_lock_status();
-
- std::string bootmode = property_get("ro.bootmode");
- if (strncmp(bootmode.c_str(), "ffbm", 4) == 0){
- property_set("ro.logdumpd","0");
- }else{
- property_set("ro.logdumpd","1");
- }
- start_property_service();
看下property_load_boot_defaults()函数:位于system/core/init/Property_service.cpp中
Java代码
-
-
- void property_load_boot_defaults() {
- load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
- }
接着继续分析main:
Java代码
- int main(int argc, char** argv) {
-
-
-
- ·
-
-
-
-
-
-
- const BuiltinFunctionMap function_map;
- Action::set_function_map(&function_map);
【结尾】
由于init涉及的知识点是相当多,代码之间的逻辑也是极其复杂,我在看别人的博客过程中,最反感一篇博客要看很久,往往因为琐事而放弃坚持(确切的说,随手把网页关掉了),所以我就分章节分析,尽量少源码多讲解。
接下来,在Android启动篇 — init原理(二)中将详细分析init.rc的解析过程。