1. init簡介
init進程是Android系統(tǒng)中用戶空間的第一個進程,進程號為1瓷式,是Android系統(tǒng)啟動流程中一個關鍵的步驟轧抗,作為第一個進程,它被賦予了很多極其重要的工作職責渡紫,比如創(chuàng)建Zygote(孵化器)和屬性服務等到推。init進程是由多個源文件共同組成的,這些文件位于源碼目錄system/core/init中惕澎。
2. 引入init進程
為了講解init進程莉测,首先要了解Android系統(tǒng)啟動流程的前幾步,以引入init進程唧喉。
1. 啟動電源以及系統(tǒng)啟動
當電源按下時引導芯片代碼開始從預定義的地方(固化在ROM)開始執(zhí)行捣卤。加載引導程序Bootloader到RAM,然后執(zhí)行八孝。
2. 引導程序Bootloader
引導程序是在Android操作系統(tǒng)開始運行前的一個小程序董朝,它的主要作用是把系統(tǒng)OS拉起來并運行。
3. linux內(nèi)核啟動
內(nèi)核啟動時干跛,設置緩存子姜、被保護存儲器、計劃列表楼入,加載驅(qū)動哥捕。當內(nèi)核完成系統(tǒng)設置,它首先在系統(tǒng)文件中尋找”init.rc”文件嘉熊,并啟動init進程遥赚。
4. init進程啟動
init進程做的工作比較多,主要用來初始化和啟動屬性服務记舆,也用來啟動Zygote進程。
3. init進程的入口函數(shù)
在Linux內(nèi)核加載完成后呼巴,它首先在系統(tǒng)文件中尋找init.rc文件泽腮,并啟動init進程,然后查看init進程的入口函數(shù)main衣赶,代碼如下所示:
system/core/init/init.cpp
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);
}
if (REBOOT_BOOTLOADER_ON_PANIC) {
install_reboot_signal_handlers();
}
add_environment("PATH", _PATH_DEFPATH);
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
// 清理 umask
umask(0);
// 創(chuàng)建和掛載啟動所需的文件目錄
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));
// 不要將原始命令行暴露給非特權(quán)進程
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));
// 初始化 Kernel的Log诊赊,這樣就可以從外界獲取Kernel的日志
InitKernelLogging(argv);
···
}
···
// 對屬性服務進行初始化
property_init(); //1
···
// 創(chuàng)建epoll句柄
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(ERROR) << "epoll_create1 failed";
exit(1);
}
// 用于設置子進程信號處理函數(shù),如果子進程(Zygote)異常退出府瞄,init進程會調(diào)用該函數(shù)中設定的信號處理函數(shù)來進行處理
signal_handler_init(); //2
// 導入默認的環(huán)境變量
property_load_boot_defaults();
export_oem_lock_status();
// 啟動屬性服務
start_property_service(); //3
set_usb_controller();
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
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", "");
if (bootscript.empty()) {
//解析init.rc配置文件
parser.ParseConfig("/init.rc"); //4
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 {
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);
}
if (false) parser.DumpState();
ActionManager& am = ActionManager::GetInstance();
···
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
// 內(nèi)部遍歷執(zhí)行每個action中攜帶的command對應的執(zhí)行函數(shù)
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
//重啟死去的進程
restart_processes(); //5
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 (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的main函數(shù)做了很多事情碧磅,比較復雜,我們只需關注主要的幾點就可以了遵馆。在開始的時候創(chuàng)建和掛載啟動所需的文件目錄鲸郊,其中掛載了 tmpfs、devpts货邓、proc秆撮、sysfs 和 selinuxfs 共5種文件系統(tǒng),這些都是系統(tǒng)運行時目錄换况,顧名思義职辨,只在系統(tǒng)運行時才會存在盗蟆,系統(tǒng)停止時會消失。
在注釋 1 處調(diào)用 property_init 函數(shù)來對屬性進行初始化舒裤,并在注釋 3 處調(diào)用 start_property_service 函數(shù)啟動屬性服務喳资。在注釋 2 處調(diào)用 signal_handler_init 函數(shù)用于設置子進程信號處理函數(shù),它被定義在 system/core/init/signal_handler.cpp 中腾供,主要用于防止init進程的子進程成為僵尸進程仆邓,為了防止僵尸進程的出現(xiàn),系統(tǒng)會在子進程暫停和終止的時候發(fā)出SIGCHLD信號台腥,而signal_handler_init函數(shù)就是用來接收SIGCHLD信號的(其內(nèi)部只處理進程終止的SIGCHLD信號)宏赘。
假設init進程的子進程Zygote終止了,signal_handler_init函數(shù)內(nèi)部會調(diào)用handle_signal函數(shù)黎侈,經(jīng)過層層的函數(shù)調(diào)用和處理察署,最終會找到Zygote進程并移除所有的Zygote進程的信息,再重啟Zygote服務的啟動腳本(比如 init.zygote64.rc)中帶有onrestart選項的服務峻汉,至于Zygote進程本身會在注釋 5 處被重啟贴汪。這里只是拿Zygote進程舉個例子,其他init進程子進程的原理也是類似的休吠。
注釋 4 處用來解析init.rc文件扳埂,解析init.rc的文件為system/core/init/init_parse.cpp文件,接下來我們查看init.rc里做了什么瘤礁。
僵尸進程與危害
在UNIX/Linux中阳懂,父進程使用fork創(chuàng)建子進程,在子進程終止之后柜思,如果父進程并不知道子進程已經(jīng)終止了岩调,這時子進程雖然已經(jīng)退出了,但是在系統(tǒng)進程表中還為它保留了一定的信息(比如進程號赡盘、退出狀態(tài)号枕、運行時間等),這個子進程就被稱作僵尸進程陨享。系統(tǒng)進程表是一項有限資源葱淳,如果系統(tǒng)進程表被僵尸進程耗盡的話,系統(tǒng)就可能無法創(chuàng)建新的進程了抛姑。
4. 解析 init.rc
init.rc 視一個非常重要的配置文件赞厕,它是由Android初始化語言(Android Init Language)編寫的腳本,這種語言主要包含5種類型語句:Action定硝、Command坑傅、Service、Option 和 Improt喷斋。init.rc 的配置代碼如下所示:
system/core/rootdir/init.rc
on init
sysclktz 0
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /default.prop /dev/urandom
...
on boot
# basic network init
ifup lo
hostname localhost
domainname localdomain
# set RLIMIT_NICE to allow priorities from 19 to -20
setrlimit 13 40 40
...
這里只截取了一部分代碼唁毒,其中#是注釋符號蒜茴。on init和on boot是Action類型語句,它的格式為:
on <trigger> [&& <trigger>]* //設置觸發(fā)器
<command>
<command> //動作觸發(fā)之后要執(zhí)行的命令
為了分析如何創(chuàng)建zygote浆西,我們主要查看Services類型語句粉私,它的格式如下所示:
service <name> <pathname> [ <argument> ]* //<service的名字><執(zhí)行程序路徑><傳遞參數(shù)>
<option> //option是service的修飾詞,影響什么時候近零、如何啟動services
<option>
...
需要注意的是诺核,Android8.0中對init.rc文件進行了拆分,每個服務對應一個rc文件久信。我們要分析的Zygote啟動腳本則在init.zygoteXX.rc中定義窖杀,這里拿64位處理器為例,init.zygote64.rc的代碼如下所示:
system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc
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用于通知init進程創(chuàng)建名zygote的進程裙士,這個zygote進程執(zhí)行程序的路徑為/system/bin/app_process64入客,后面的則是要傳給app_process64的參數(shù)。class main指的是zygote的class name為main腿椎,后文會用到它桌硫。
5. 解析service類型語句
init.rc中的Action類型語句和Service類型語句都有相應的類來進行解析,Action類型語句采用ActionParser來進行解析啃炸,Service類型語句采用ServiceParser來進行解析铆隘,這里因為主要分析Zygote,所以只介紹ServiceParser南用。ServiceParser的實現(xiàn)代碼在system/core/init/service.cpp中膀钠,接下來我們來查看ServiceParser是如何解析上面提到的Service類型語句的,會用到兩個函數(shù):一個是ParseSection裹虫,它會解析Service的rc文件肿嘲,比如上文講到的init.zygote64.rc,ParseSetion函數(shù)主要用來搭建Service的架子恒界;另一個是PareLineSection睦刃,用于解析子項砚嘴。代碼如下所示:
system/core/init/service.cpp
bool ServiceParser::ParseSection(const std::vector<std::string>& args,
std::string* err) {
if (args.size() < 3) { //判斷Service是否有name與執(zhí)行程序
*err = "services must have a name and a program";
return false;
}
const std::string& name = args[1];
if (!IsValidName(name)) { //檢查Service的name是否有效
*err = StringPrintf("invalid service name '%s'", name.c_str());
return false;
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, "default", str_args); //1
return true;
}
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const {
return service_ ? service_->HandleLine(args, err) : false;
}
注釋 1 處十酣,根據(jù)參數(shù),構(gòu)造出一個Service對象际长,它的classname為default耸采。在解析完所有數(shù)據(jù)后,會調(diào)用EndSection:
void ServiceParser::EndSection() {
if (service_) {
ServiceManager::GetInstance().AddService(std::move(service_));
}
}
EndSection函數(shù)中會調(diào)用ServiceManager的AddService函數(shù)工育,接著查看AddService做了什么:
void ServiceManager::AddService(std::unique_ptr<Service> service) {
Service* old_service = FindServiceByName(service->name());
if (old_service) {
ERROR("ignored duplicate definition of service '%s'",
service->name().c_str());
return;
}
services_.emplace_back(std::move(service)); //1
}
注釋 1 處的代碼將service對象加入到services鏈表中虾宇。上面的解析過程總體來講就是根據(jù)參數(shù)創(chuàng)建出service對象,然后根據(jù)選項域的內(nèi)容填充service對象如绸,最后將service對象加入到vector類型的services鏈表中嘱朽。