本文使用的軟體版本
Android:4.2.2
Linux核心:3.1.10
本文及後續幾篇文章將對Android的初始化(init)過程進行詳細地、剝絲抽繭式地分析,並且在其中穿插了大量的知識,希望對讀者瞭解Android的啟動過程又所協助。本章主要介紹了與硬體相關初始設定檔案名的確定以及屬性服務的原理和實現。
Android本質上就是一個基於Linux核心的作業系統。與Ubuntu Linux、Fedora Linux類似。只是Android在應用程式層專門為行動裝置添加了一些特有的支援。既然Android是Linux核心的系統,那麼基本的啟動過程也應符 合Linux的規則。如果研究過其他Linux系統應該瞭解,一個完整的Linux系統首先會將一個Linux核心裝載到記憶體,也就是編譯Linux核心 原始碼產生的bzImage檔案,對於為Android最佳化的Linux核心原始碼會產生zImage檔案。該檔案就是Linux核心的二進位版本。由於 zImage在核心空間運行,而我們平常使用的軟體都是在應用空間運行(關於核心空間和應用空間的詳細描述,可以參考《Android深度探索(卷1):HAL與驅動開發》一書的內容,在後續的各卷中將會對Android的整體體系進行全方位的剖析)。核心空間和應用空間是不能直接通過記憶體位址層級訪問的,所以就需要建立某種通訊機制。
目前Linux有很多通訊機制可以在使用者空間和核心空間之間互動,例如裝置驅動檔案(位於/dev目錄中)、記憶體檔案(/proc、/sys目錄等)。了 解Linux的同學都應該知道Linux的重要特徵之一就是一切都是以檔案的形式存在的,例如,一個裝置通常與一個或多個裝置檔案對應。這些與核心空間交 互的檔案都在使用者空間,所以在Linux核心裝載完,需要首先建立這些檔案所在的目錄。而完成這些工作的程式就是本文要介紹的init。Init是一個命 令行程式。其主要工作之一就是建立這些與核心空間互動的檔案所在的目錄。當Linux核心載入完後,要做的第一件事就是調用init程式,也就是 說,init是使用者空間執行的第一個程式。
在分析init的核心代碼之前,還需要初步瞭解init除了建立一些目錄外,還做了如下的工作
1. 初始化屬性
2. 處理設定檔的命令(主要是init.rc檔案),包括處理各種Action。
3. 效能分析(使用bootchart工具)。
4. 無限迴圈執行command(啟動其他的進程)。
儘管init完成的工作不算很多,不過代碼還是非常複雜的。Init程式並不是由一個原始碼檔案組成的,而是由一組原始碼檔案的目標檔案連結而成的。這些檔案位於如下的目錄。
<Android原始碼本目錄>/system/core/init
其中init.c是init的主檔案,現在開啟該檔案,看看其中的內容。由於init是命令列程式,所以分析init.c首先應從main函數開始,現在好到main函數,代碼如下:
int main(int argc, char **argv) { int fd_count = 0; struct pollfd ufds[4]; char *tmpdev; char* debuggable; char tmp[32]; int property_set_fd_init = 0; int signal_fd_init = 0; int keychord_fd_init = 0; bool is_charger = false; 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); // 下面的代碼開始建立各種使用者空間的目錄,如/dev、/proc、/sys等 mkdir("/dev", 0755); mkdir("/proc", 0755); mkdir("/sys", 0755); 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); /* 檢測/dev/.booting檔案是否可讀寫和建立*/ close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000)); open_devnull_stdio(); klog_init(); // 初始化屬性 property_init(); get_hardware_name(hardware, &revision); // 處理核心命令列 process_kernel_cmdline(); … … is_charger = !strcmp(bootmode, "charger"); INFO("property init\n"); if (!is_charger) property_load_boot_defaults(); INFO("reading config file\n"); // 分析/init.rc檔案的內容 init_parse_config_file("/init.rc"); … …// 執行初始設定檔案中的動作 action_for_each_trigger("init", action_add_queue_tail); // 在charger模式下略過mount檔案系統的工作 if (!is_charger) { action_for_each_trigger("early-fs", action_add_queue_tail); action_for_each_trigger("fs", action_add_queue_tail); action_for_each_trigger("post-fs", action_add_queue_tail); action_for_each_trigger("post-fs-data", action_add_queue_tail); } queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(check_startup_action, "check_startup"); if (is_charger) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", 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"); #if BOOTCHART queue_builtin_action(bootchart_init_action, "bootchart_init"); #endif // 進入無限迴圈,建立init的子進程(init是所有進程的父進程) for(;;) { int nr, i, timeout = -1; // 執行命令(子進程對應的命令) execute_one_command(); restart_processes(); if (!property_set_fd_init && get_property_set_fd() > 0) { ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; property_set_fd_init = 1; } if (!signal_fd_init && get_signal_fd() > 0) { ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; signal_fd_init = 1; } if (!keychord_fd_init && get_keychord_fd() > 0) { ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; keychord_fd_init = 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是一個效能統計工具,用於搜集硬體和系統的資訊,並將其寫入磁碟,以便其 // 他程式使用 #if BOOTCHART if (bootchart_count > 0) { if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) timeout = BOOTCHART_POLLING_MS; if (bootchart_step() < 0 || --bootchart_count == 0) { bootchart_finish(); bootchart_count = 0; } } #endif // 等待下一個命令的提交 nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; for (i = 0; i < fd_count; i++) { if (ufds[i].revents == POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); else if (ufds[i].fd == get_signal_fd()) handle_signal(); } } } return 0; }