介紹
這是一個連載的系列「圖解 Android 系列」,我將持續為大家提供儘可能通俗易懂的 Android 原始碼分析。
所有引用的原始碼片段,我都會在第一行標明原始檔完整路徑。為了文章篇幅考慮原始碼中間可能有刪減,刪減部分會用省略號代替。
本系列原始碼基於:Android Oreo(8.0)
init 程式
在上篇文章 揭祕 Android 系統啟動過程 中介紹到,init 程式啟動分為前後兩部分,前一部分是在核心啟動的,主要是完成建立和核心初始化工作,內容都是跟 Linux 核心相關的;後一部分是在使用者空間啟動的,主要完成 Android 系統的初始化工作。
Android 系統一般會在根目錄下放一個 init 的可執行檔案,也就是說 Linux 系統的 init 程式在核心初始化完成後,就直接執行 init 這個檔案,這個檔案的原始碼在 /system/core/init/init.cpp
。
init 程式是 Linux 系統中使用者空間的第一個程式(pid = 1),我們熟悉的 App 應用程式都是以它為父程式的,init 程式入口函式是 main 函式。這個函式做的事情還是比較多的,主要分為三個部分:
- init 程式第一階段
- init 程式第二階段
- init.rc 檔案解析
第一階段
我們先來看第一階段主要有以下內容:。
- ueventd/watchdogd 跳轉及環境變數設定。
- 掛載檔案系統並建立目錄。
- 初始化日誌輸出、掛載分割槽裝置。
- 啟用 SELinux 安全策略。
- 開始第二階段前的準備。
//system/core/init/init.cpp
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
// 1 表示 true,也就執行 ueventd_main,ueventd
// 主要是負責裝置節點的建立、許可權設定等一些列工作
return ueventd_main(argc, argv);
}
// watchdogd 俗稱看門狗,用於系統出問題時重啟系統
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
if (REBOOT_BOOTLOADER_ON_PANIC) {
//初始化重啟系統的處理訊號,內部通過 sigaction 註冊訊號,
//當監聽到該訊號時重啟系統
install_reboot_signal_handlers();
}
//註冊環境變數PATH
add_environment("PATH", _PATH_DEFPATH);
// init 的 main 方法會執行兩次,由 is_first_stage 控制,
// first_stage 就是第一階段要做的事
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
// 只執行一次,因為在方法體中有設定 INIT_SECOND_STAGE
if (is_first_stage) {
// 清空檔案許可權
umask(0);
// 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));
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
// 初始化日誌輸出
InitKernelLogging(argv);
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic();
}
// 在刷機模式下初始化avb的版本,不是刷機模式直接跳過
SetInitAvbVersionInRecovery();
// 載入S ELinux policy,也就是安全策略
selinux_initialize(true);
// 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 (restorecon("/init") == -1) {
PLOG(ERROR) << "restorecon failed";
security_failure(); // 失敗則重啟系統
}
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", StringPrintf("%" PRIu64, start_ms).c_str(), 1);
char* path = argv[0];
char* args[] = { path, nullptr };
execv(path, args); // 重新執行 main 方法,進入第二階段
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(ERROR) << "execv(\"" << path << "\") failed";
security_failure();
}
// ...
}
複製程式碼
init 程式第一階段做的主要工作是掛載分割槽,建立裝置節點和一些關鍵目錄,初始化日誌輸出系統,啟用 SELinux 安全策略。
第二階段
我們接著看第二階段,主要有以下內容:
- 建立程式會話金鑰並初始化屬性系統。
- 進行 SELinux 第二階段並恢復一些檔案安全上下文。
- 新建 epoll 並初始化子程式終止訊號處理函式。
- 設定其他系統屬性並開啟系統屬性服務。
//system/core/init/init.cpp
int main(int argc, char** argv) {
// 同樣進行 ueventd/watchdogd 跳轉及環境變數設定
// 之前準備工作時將 INIT_SECOND_STAGE設 置為 true,
// 已經不為 nullptr,所以 is_first_stage 為 false
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
// is_first_stage為false,直接跳過
if (is_first_stage) {
// ...
}
// 初始化日誌輸出
InitKernelLogging(argv);
// ...
// 初始化屬性系統,並從指定檔案讀取屬性
property_init();
// ...
// 初始化子程式退出的訊號處理函式
signal_handler_init();
// 載入 default.prop 檔案
property_load_boot_defaults();
export_oem_lock_status();
// 啟動屬性伺服器
start_property_service();
set_usb_controller();
//...
}
複製程式碼
init 程式第二階段主要工作是初始化屬性系統,解析 SELinux 的匹配規則,處理子程式終止訊號,啟動系統屬性服務,可以說每一項都很關鍵。如果說第一階段是為屬性系統、SELinux 做準備,那麼第二階段就是真正去把這些落實的。
解析 init.rc 檔案
//system/core/init/init.cpp
int main(int argc, char** argv) {
// ...
const BuiltinFunctionMap function_map;
// 將 function_map 存放到 Action 中作為成員屬性
Action::set_function_map(&function_map);
// 解析 init.rc 檔案
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
std::string bootscript = GetProperty("ro.boot.init_rc", "");
// 如果 ro.boot.init_rc 沒有對應的值,
// 則解析 /init.rc 以及 /system/etc/init、/vendor/etc/init、
// /odm/etc/init 這三個目錄下的 .rc 檔案
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(
parser.ParseConfig("/odm/etc/init"));
} else { // 如果 ro.boot.init_rc 屬性有值就解析屬性值
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}
// ...
ActionManager& am = ActionManager::GetInstance();
am.QueueEventTrigger("early-init");
// 等冷插拔裝置初始化完成
am.QueueBuiltinAction(wait_for_coldboot_done_action,
"wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
// 裝置組合鍵的初始化操作
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
// 螢幕上顯示 Android 靜態 Logo,很熟悉的感覺有沒有
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
// 執行 rc 檔案中觸發器為 on init 的語句
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng");
// 當處於充電模式,則 charger 加入執行佇列,否則 late-init 加入佇列。
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init"); // 觸發 late-init
}
// 觸發器為屬性是否設定
am.QueueBuiltinAction(queue_property_triggers_action,
"queue_property_triggers");
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
if (!(waiting_for_prop
|| ServiceManager::GetInstance().IsWaitingForExec())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop
|| ServiceManager::GetInstance().IsWaitingForExec())) {
// 根據需要重啟服務
restart_processes();
// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {
epoll_timeout_ms =
(process_needs_restart_at - time(nullptr)) * 1000;
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;
// 迴圈等待事件發生
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)();
}
}
return 0;
}
複製程式碼
這一階段 init 程式做了許多重要的事情,比如解析 init.rc 檔案,這裡配置了所有需要執行的 action 和需要啟動的 service,init 程式根據語法一步步去解析 init.rc,將這些配置轉換成一個個陣列、佇列,然後開啟無限迴圈去處理這些陣列、佇列中的 command 和 service,並且通過 epoll 監聽子程式結束和屬性設定。
init.rc 檔案
init.rc 檔案是 Android 系統的重要配置檔案,位於 /system/core/rootdir/
目錄中。 主要功能是定義了系統啟動時需要執行的一系列 action 及執行特定動作、設定環境變數和屬性和執行特定的 service。
//system/core/rootdir/init.rc
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc // 稍後分析
on early-init
// ...
on init
// ...
on late-init
// ...
trigger zygote-start
on post-fs // 掛載檔案系統
load_system_props
# start essential services
start logd
// 熟悉的 servermanager,後面章節再討論
start servicemanager
start hwservicemanager
start vndservicemanager
// ...
on post-fs-data // 掛載 data
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
chmod 0771 /data
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
# Make sure we have the device encryption key.
start vold
// ...
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted // 啟動 zygote,稍後分析
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on boot
// ...
# Start standard binderized HAL daemons
class_start hal
class_start core
複製程式碼
init 程式會解析 .rc
檔案,然後得到一些 service 去啟動,這些 service 通常不是普通的服務,文件裡面的稱呼是daemon(守護程式)。
所謂守護程式就是這些服務程式會在系統初始化時啟動,並一直執行於後臺,直到系統關閉時終止。
到這裡 init 程式的主要流程已經分析完了,我們總結下 init 程式啟動主要做了哪些工作。
首先在 Kernel 核心載入完後會呼叫 /system/core/init/init.cpp
檔案中的 main()
方法。該方法執行分為三個階段,前兩個的階段都是初始化環境,我們主要關注下第三個階段 解析 .rc
檔案。
在第三階段中通過解析 .rc 檔案啟動了 servicemanager
和 zygote
等服務,最後 init 程式進入了 loop。
zygote 程式
zygote 程式就是 daemon 其中之一,zygote 程式主要負責建立 Java 虛擬機器,載入系統資源,啟動 SystemServer 程式,以及在後續執行過程中啟動普通的應用程式。
在 init.rc 檔案頭部有這麼一句:
import /init.${ro.zygote}.rc
複製程式碼
其中 ${ro.zygote}
會被替換成 ro.zyogte 的屬性值,這個是由不同的硬體廠商自己定製的。 有四個值:zygote32、zygote64、zygote32_64、zygote64_32 ,也就是說可能有四種 .rc 檔案,分別是:
- init.zygote32.rc:zygote 程式對應的執行程式是 app_process(純 32bit 模式)。
- init.zygote64.rc:zygote 程式對應的執行程式是 app_process64(純 64bit 模式)。
- init.zygote32_64.rc:啟動兩個 zygote 程式(名為 zygote 和 zygote_secondary),對應的執行程式分別是 app_process32(主模式)、app_process64。
- init.zygote64_32.rc:啟動兩個 zygote 程式(名為 zygote 和 zygote_secondary),對應的執行程式分別是 app_process64(主模式)、app_process32。
為什麼要定義這麼多種情況呢?直接定義一個不就好了,這主要是因為 Android 5.0 以後開始支援 64 位程式,為了相容 32 位和 64 位才這樣定義。
不同的 zygote.rc 內容大致相同,主要區別體現在啟動的是 32 位,還是 64 位的程式。init.zygote32_64.rc 和 init.zygote64_32.rc 會啟動兩個程式,且存在主次之分。我們以init.zygote64_32.rc
為例。
//system/core/rootdir/init.zygote64_32.rc
// 程式名稱是 zygote,執行的二進位制檔案在 /system/bin/app_process64,稍後分析
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
group root readproc
//建立一個 socket,名字叫 zygote,以 tcp 形式
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
// 另一個 service,名字 zygote_secondary
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
priority -20
user root
group root readproc
socket zygote_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
複製程式碼
在 init.rc 檔案中可以找到一句 start zygote
,這是呼叫 zygote 服務的啟動方式。
//system/core/rootdir/init.rc
// ...
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
// 啟動 zygote,稍後分析
on zygote-start && property:ro.crypto.state=unencrypted
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
複製程式碼
在 init.zygote64_32.rc 檔案中的頭部我們可以看到 zygote 對應的二進位制檔案是 /system/bin/app_process64
(以此為例),我們看一下對應的mk檔案, 對應的目錄在 platform/frameworks/base/cmds/app_process/Android.mk
,其實不管是 app_process、app_process32 還是 app_process64,對應的原始檔都是 app_main.cpp。
//frameworks/base/cmds/app_process/Android.mk
// ...
app_process_src_files := \
app_main.cpp \
// ...
LOCAL_SRC_FILES:= $(app_process_src_files)
// ...
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
// ...
複製程式碼
app_main.cpp
在 app_main.cpp 的 main 函式中,主要做的事情就是引數解析。 這個函式有兩種啟動模式:
- 一種是 zygote 模式,也就是初始化 zygote 程式,傳遞的引數有 --start-system-server --socket-name=zygote,前者表示啟動 SystemServer,後者指定 socket 的名稱。
- 一種是 application 模式,也就是啟動普通應用程式,傳遞的引數有 class 名字以及 class 帶的引數。
兩者最終都是呼叫 AppRuntime 物件的 start 函式,載入 ZygoteInit 或 RuntimeInit 兩個 Java 類,並將之前整理的引數傳入進去。
//frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]) {
// 將引數 argv 放到 argv_String 字串中,然後列印出來
if (!LOG_NDEBUG) {
String8 argv_String;
for (int i = 0; i < argc; ++i) {
argv_String.append("\"");
argv_String.append(argv[i]);
argv_String.append("\" ");
}
}
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// 所有在 "--" 後面的非 "-" 開頭的引數都將傳入 vm,
// 但是有個例外是 spaced commands 陣列中的引數
const char* spaced_commands[] = { "-cp", "-classpath" };
// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
bool known_command = false;
int i;
for (i = 0; i < argc; i++) {
// 將 spaced_commands 中的引數額外加入 VM
if (known_command == true) {
runtime.addOption(strdup(argv[i]));
known_command = false;
continue;
}
for (int j = 0;
j < static_cast<int>(sizeof(spaced_commands)
/ sizeof(spaced_commands[0]));
++j) {
if (strcmp(argv[i], spaced_commands[j]) == 0) {
known_command = true;
}
}
if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
runtime.addOption(strdup(argv[i]));
}
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
// 表示是 application 啟動模式,也就是普通應用程式
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
// 程式別名
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
// application 啟動的 class
className.setTo(arg);
break;
} else {
--i;
break;
}
}
Vector<String8> args;
if (!className.isEmpty()) {
// className 不為空,說明是 application 啟動模式
args.add(application ? String8("application") : String8("tool"));
// 將 className 和引數設定給 runtime
runtime.setClassNameAndArgs(className, argc - i, argv + i);
if (!LOG_NDEBUG) {
String8 restOfArgs;
char* const* argv_new = argv + i;
int argc_new = argc - i;
for (int k = 0; k < argc_new; ++k) {
restOfArgs.append("\"");
restOfArgs.append(argv_new[k]);
restOfArgs.append("\" ");
}
}
} else { // zygote 啟動模式
// We're in zygote mode.
maybeCreateDalvikCache(); // 新建 Dalvik 的快取目錄
if (startSystemServer) { // 加入 start-system-server 引數
args.add(String8("start-system-server"));
}
char prop[PROP_VALUE_MAX];
if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
ABI_LIST_PROPERTY);
return 11;
}
String8 abiFlag("--abi-list=");
abiFlag.append(prop);
args.add(abiFlag); // 加入 --abi-list= 引數
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i]));
}
}
if (!niceName.isEmpty()) { // 設定程式別名
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) { // 如果是 zygote 啟動模式,則載入 ZygoteInit
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
// 如果是 application 啟動模式,則載入 RuntimeInit
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.");
}
}
複製程式碼
我們看到,在最後呼叫的是 runtime.start 函式,這個就是要啟動虛擬機器了,接下來我們分析 start 函式。
建立虛擬機器
//frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className,
const Vector<String8>& options, bool zygote) {
// ...
// 列印一些日誌,獲取 ANDROID_ROOT 環境變數
// ...
/* start the virtual machine */
JniInvocation jni_invocation;
// 初始化JNI,載入 libart.so
jni_invocation.Init(NULL);
JNIEnv* env;
// 建立虛擬機器
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
// 表示虛擬建立完成,但是裡面是空實現
onVmCreated(env);
/*
* Register android functions.
* 註冊 JNI 函式
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
// JNI 方式呼叫 ZygoteInit 類的 main 函式
// ...
}
複製程式碼
虛擬機器建立完成後,我們就可以用 JNI 反射呼叫 Java 了,其實接下來的語法用過 JNI 的都應該比較熟悉了,直接是 CallStaticVoidMethod 反射呼叫 ZygoteInit 的 main 函式。
//frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options,
bool zygote) {
// 接下來的這些語法大家應該比較熟悉了,都是 JNI 裡的語法,
// 主要作用就是呼叫 ZygoteInit 類的 main 函式
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
// 將 "com.android.internal.os.ZygoteInit"
// 轉換為 "com/android/internal/os/ZygoteInit"
char* slashClassName = toSlashClassName(className);
// 找到 class
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"[Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {// 呼叫 ZygoteInit.main() 方法
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
ALOGD("Shutting down VM\n");
// 退出當前執行緒
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread\n");
// 建立一個執行緒,該執行緒會等待所有子執行緒結束後關閉虛擬機器
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly\n");
}
複製程式碼
zygote 程式啟動主要建立了 Java 虛擬機器,有了虛擬機器,就可以執行 Java 程式碼了。
ZygoteInit.java
//frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
// 標記 zygote 程式已啟動
ZygoteHooks.startZygoteNoThreadCreation();
// 進入 Zygote 程式
try {
Os.setpgid(0, 0);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
try {
RuntimeInit.enableDdms(); // 開啟 DDMS 功能
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: "
+ argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
// 為 zygote 註冊 socket
zygoteServer.registerServerSocket(socketName);
// 預載入處理
if (!enableLazyPreload) {
// zygote 預載入,下面介紹
preload(bootTimingsTraceLog);
} else {
Zygote.resetNicePriority();
}
gcAndFinalize(); // GC 操作
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
// Set seccomp policy
Seccomp.setPolicy();
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) { // 啟動 system_server,下面介紹
startSystemServer(abiList, socketName, zygoteServer);
}
// 進入迴圈模式,下面介紹
zygoteServer.runSelectLoop(abiList);
zygoteServer.closeServerSocket();
} catch (Zygote.MethodAndArgsCaller caller) {
caller.run();
} catch (Throwable ex) {
zygoteServer.closeServerSocket();
throw ex;
}
}
複製程式碼
這裡 startSystemServer()
方法會丟擲一個 Zygote.MethodAndArgsCaller
異常,然後呼叫到 caller.run()
,這裡會在 SystemServer 章節繼續分析。
preload()
//frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
// ...
// 預載入位於 /system/etc/preloaded-classes 檔案中的類
preloadClasses();
// 預載入資源,包含 drawable 和 color 資源
preloadResources();
// 預載入 OpenGL
preloadOpenGL();
// 通過 System.loadLibrary() 方法,
// 預載入 "android","compiler_rt","jnigraphics" 這 3 個共享庫
preloadSharedLibraries();
// 預載入 文字連線符資源
preloadTextResources();
// 僅用於 zygote 程式,用於記憶體共享的程式
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders();
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
複製程式碼
執行 zygote 程式的初始化,對於類載入,採用反射機制 Class.forName()
方法來載入。對於資源載入,主要是 com.android.internal.R.array.preloaded_drawables 和 com.android.internal.R.array.preloaded_color_state_lists,在應用程式中以 com.android.internal.R.xxx 開頭的資源,便是此時由 Zygote 載入到記憶體的。
zygote 程式內載入了 preload() 方法中的所有資源,當需要 fork 新程式時,採用 copy on write 技術,如下:
startSystemServer()
//frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static boolean startSystemServer(String abiList,
String socketName, ZygoteServer zygoteServer)
throws Zygote.MethodAndArgsCaller, RuntimeException {
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_IPC_LOCK,
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_PTRACE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG,
OsConstants.CAP_WAKE_ALARM
);
/* Containers run without this capability, so avoid setting it in that case */
if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER,
false)) {
capabilities |=
posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND);
}
/* Hardcoded command line to start the system server */
// 準備引數
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
// 用於解析引數,生成目標格式
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
// fork 子程式,用於執行 system_server
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
// 進入子程式 system_server
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
// 完成 system_server 程式剩餘的工作
handleSystemServerProcess(parsedArgs);
}
return true;
}
複製程式碼
可以看到 zygote 程式 fork 出一個新程式名為 system_server
也就是 SystemServer 程式。該方法是 SystemServer 程式啟動的起點,關於 SystemServer 啟動流程我們在後面的章節在討論,這裡先分析完 zygote 的主要流程。
runSelectLoop()
//frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
// mServerSocket 是 socket 通訊中的服務端,即 zygote 程式。儲存到 fds[0]
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
// 處理輪詢狀態,當 pollFds 有事件到來則往下執行,否則阻塞在這裡
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
// 採用 I/O 多路複用機制,當接收到客戶端發出連線請求
// 或者資料處理請求到來,則往下執行;
// 否則進入continue,跳出本次迴圈。
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
// i>0,則代表通過 socket 接收來自對端的資料,並執行相應操作
boolean done = peers.get(i).runOnce(this);
if (done) {
peers.remove(i);
fds.remove(i); // 處理完則從 fds 中移除該檔案描述符
}
}
}
}
}
複製程式碼
zygote 採用高效的 I/O 多路複用機制,保證在沒有客戶端連線請求或資料處理時休眠,否則響應客戶端的請求。在呼叫 runSelectLoop()
後 zygote 進入了輪詢狀態,隨時待命當接收到請求建立新程式請求時,立即喚醒並執行相應工作。
總結
到這裡 zygote 程式已經啟動完成了,Android 系統到目前已經啟動了第一個使用者程式 zygote。我們來回顧下目前 Android 系統啟動的流程:
當我們按下電源按鍵後會啟動 Bootloader,Bootloader 會載入 Kernel 核心到記憶體中,然後會啟動核心中的 idle 程式,idle 程式會啟動 kthreadd 和 init 兩個程式。
init 程式在啟動的時候會初始化執行環境,然後解析 init.rc 檔案,解析 init.rc 檔案的過程中會啟動 servicemanager、zygote 等服務,最後進入 loop 狀態。
zygote 服務啟動時會建立 Java 虛擬機器並初始化 Java 執行環境,然後啟動 SystemServer 服務,最後進入 loop 狀態。
這篇文章我們分析到這裡,下一篇我們接著分析 zygote 程式啟動中呼叫 startSystemServer() 方法後 SystemServer 程式啟動的流程。
參考資料
- Android核心開發:圖解Android系統的啟動過程
- Android 8.0 : 系統啟動流程之Linux核心
- Android bootloader/fastboot mode and recovery mode explained/Android boot process
- Android is NOT just 'Java on Linux'
- Android 啟動流程簡介
我的 Github
我的公眾號
歡迎關注我的公眾號,分享各種技術乾貨,各種學習資料,職業發展和行業動態。
技術交流群
歡迎加入技術交流群,來一起交流學習。