从源码角度看Android系统init进程启动过程
init進程是Linux系統中用戶空間的第一個進程,進程號為1。Kernel啟動后,在用戶空間啟動init進程,并調用/system/core/init.cpp中的main方法執行一些重要的工作。
備注:本文將結合Android8.0的源碼看init進程的啟動過程以及init進程做了哪些重要工作。
1. init進程啟動前系統的啟動流程
在引入init進程前,我們需要大致了解系統是如何走到init進程的。大致步驟如下:
啟動電源和系統啟動
按下電源,讓設備開機時引導芯片代碼會從預定義的地方開始執行。加載引導程序BootLoader到RAM中。
BootLoader
BootLoader只是Android系統開始前的一個引導程序,主要作用是將OS系統拉起來并運行。
啟動Linux內核
當內核啟動時,會先去設置緩存、加載驅動等,在系統設置完成后,會在系統文件中尋找init.rc文件,并啟動init進程。
啟動init進程
經過前面的步驟,到這一步,init就被正式啟動。
init進程啟動后,下面就看下它里面的一些重要的職責工作。
2. init進程main函數
main函數是init進程的入口函數,代碼位置在:
/system/core/init.cpp深入到init.cpp的main函數中:
int main(int argc, char** argv) {...省略...if (is_first_stage) {boot_clock::time_point start_time = boot_clock::now();// Clear the umask.umask(0);//創建和掛載啟動所需的文件目錄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));//初始化Kernel的LogInitKernelLogging(argv);...省略...}...省略...//創建一塊共享的內存空間,用于對屬性服務進行初始化property_init();...省略...//創建epoll句柄epoll_fd = epoll_create1(EPOLL_CLOEXEC);if (epoll_fd == -1) {PLOG(ERROR) << "epoll_create1 failed";exit(1);}//初始化子進程退出的信號處理函數//如果子進程異常退出,init進程會調用該函數中設定的信號處理函數來進程處理//防止子進程成為僵尸進程signal_handler_init();//加載default.prop文件,導入默認的環境變量property_load_boot_defaults();export_oem_lock_status();//啟動屬性服務器,會調用epoll_ctl設置property fd可讀的回調函數start_property_service();...省略...if (bootscript.empty()) {//解析init.rc文件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 {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);}...省略...//不要在充電器模式下掛載文件系統或啟動核心系統服務std::string bootmode = GetProperty("ro.bootmode", "");if (bootmode == "charger") {am.QueueEventTrigger("charger");} else {am.QueueEventTrigger("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進程執行完成后,就進入循環等待epoll_wait的狀態。init的main函數中做了大量初始化工,比較復雜。我主要學習了如下幾個關鍵點:
創建和掛載啟動的文件
屬性服務
進程信號處理
解析init.rc文件
init啟動Zygote
3. 創建和掛載啟動的文件
在最開始的時候就創建和掛載啟動所需要的文件目錄,其中掛載了:
-
tempfs
-
devpts
-
proc
-
sysfs
-
selinuxfs
共掛載了這五個系統,這些都是系統運行時的目錄,只有當系統運行時才會存在,當系統停止時,這些目錄就不會存在。
4. 屬性服務
在Windows上有一個注冊表管理器,主要采用的是鍵值對的形式來記錄用戶、軟件的一些信息。即使系統或軟件重啟,還是能根據之前注冊表中的記錄,進行初始化工作。在Android系統中,也有一個類似的機制,叫作屬性服務。
當某個進程通過property_set()方法修改屬性后,init進程會先檢查權限,當權限驗證通過后,就會去修改相應的屬性值,而屬性值一旦改變,就會觸發相應的觸發器(即rc文件中的on開頭的語句),在Android Shared Memmory(共享內存區)中有一個_system_property_area_區域,里面存儲這所有的屬性值。某個進程通過property_get()方法,獲取的也是這個共享內存區域的屬性值。
在init.cpp的main函數中與屬性服務相關的代碼有如下兩行:
路徑:system/core/init/init.cpp
property_init();start_property_service();這兩行代碼主要是初始化屬性服務配置并啟動屬性服務。
4.1 初始化共享內存并啟動屬性服務
4.1.1 property_init
深入到/system/core/init/property_service.cpp中:
void property_init() {if (__system_property_area_init()) {LOG(ERROR) << "Failed to initialize property area";exit(1);} }這個方法的主要核心是執行__system_property_area_init()方法,初始化跨進程的共享內存。
4.1.2 start_property_service
深入到/system/core/init/property_service.cpp中:
void start_property_service() {property_set("ro.property_service.version", "2");property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,0666, 0, 0, NULL);//注釋1if (property_set_fd == -1) {PLOG(ERROR) << "start_property_service socket creation failed";exit(1);}listen(property_set_fd, 8);//注釋2register_epoll_handler(property_set_fd, handle_property_set_fd);//注釋3 }注釋解析說明:
注釋1:創建名為“property_service”的Socket。
注釋2:調用listen函數對property_set_fd進行監聽,這里的Socket就成為了server,即屬性服務。其中,參數8表示可以同時為8個試圖設置屬性的用戶提供服務。
注釋3:將property_set_fd放入到epoll中,用epoll來監聽property_set_fd,當property_set_fd中有數據變動時,init進程將調用handle_property_set_fd函數進行處理。
其中,epoll是Linux內核為處理大批量文件描述符而做了改進的poll,能顯著提高程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率。
4.2 服務端請求屬性服務處理數據
上面說了當客戶端請求屬性服務處理數據時,會調用handle_property_set_fd函數進行處理。
4.2.1 handle_property_set_fd
深入到/system/core/init/property_service.cpp的handle_property_set_fd方法中:
static void handle_property_set_fd() {static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */...省略... uint32_t timeout_ms = kDefaultSocketTimeout;//設置2秒超時uint32_t cmd = 0;if (!socket.RecvUint32(&cmd, &timeout_ms)) {PLOG(ERROR) << "sys_prop: error while reading command from the socket";socket.SendUint32(PROP_ERROR_READ_CMD);return;}switch (cmd) {case PROP_MSG_SETPROP: {char prop_name[PROP_NAME_MAX];char prop_value[PROP_VALUE_MAX];//如果2秒內Socket沒有讀取到數據則直接返回if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||!socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";return;}prop_name[PROP_NAME_MAX-1] = 0;prop_value[PROP_VALUE_MAX-1] = 0;//調用handle_property_set方法去處理數據handle_property_set(socket, prop_value, prop_value, true);break;}...省略...} }從上面可以看到Android8.0繼續封裝了handle_property_set方法去處理數據。
4.2.2 handle_property_set
深入到/system/core/init/property_service.cpp的handle_property_set方法中:
static void handle_property_set(SocketConnection& socket,const std::string& name,const std::string& value,bool legacy_protocol) {const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";if (!is_legal_property_name(name)) {//檢查屬性名是否合法,不合法直接returnLOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";socket.SendUint32(PROP_ERROR_INVALID_NAME);return;}struct ucred cr = socket.cred();char* source_ctx = nullptr;getpeercon(socket.socket(), &source_ctx);//如果屬性名稱以“ctl.”開頭,則說明是控制屬性if (android::base::StartsWith(name, "ctl.")) {//檢查權限if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {//如果滿足權限,則直接設置以ctl.開頭的屬性handle_control_message(name.c_str() + 4, value.c_str());if (!legacy_protocol) {socket.SendUint32(PROP_SUCCESS);}} else {LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)<< " service ctl [" << value << "]"<< " uid:" << cr.uid<< " gid:" << cr.gid<< " pid:" << cr.pid;if (!legacy_protocol) {socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);}}} else {//不是以“ctl.”開頭,則說明是普通屬性if (check_mac_perms(name, source_ctx, &cr)) {//調用property_set方法來設置普通屬性uint32_t result = property_set(name, value);if (!legacy_protocol) {socket.SendUint32(result);}} else {LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;if (!legacy_protocol) {socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);}}}freecon(source_ctx); }綜上所述,會先檢查屬性名是否合法,在滿足合法的條件后;判斷屬性名是否是以“ctl.”開頭;如果是,則通過handle_control_message去設值;如果不是,則通過調用property_set方法來設值。
4.2.3 property_set
深入到/system/core/init/property_service.cpp的property_set方法中:
uint32_t property_set(const std::string& name, const std::string& value) {size_t valuelen = value.size();//判斷屬性名是否合法if (!is_legal_property_name(name)) {LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";return PROP_ERROR_INVALID_NAME;}//屬性名不能過長if (valuelen >= PROP_VALUE_MAX) {LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "<< "value too long";return PROP_ERROR_INVALID_VALUE;}if (name == "selinux.restorecon_recursive" && valuelen > 0) {if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {LOG(ERROR) << "Failed to restorecon_recursive " << value;}}//從屬性存儲空間中查找該屬性prop_info* pi = (prop_info*) __system_property_find(name.c_str());//屬性如果存在if (pi != nullptr) {// 如果屬性名是以“.ro”開頭,表示只讀,不能修改,直接返回if (android::base::StartsWith(name, "ro.")) {LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "<< "property already set";return PROP_ERROR_READ_ONLY_PROPERTY;}// 如果屬性存在,則就去更新__system_property_update(pi, value.c_str(), valuelen);} else {//屬性如果不存在,則添加該屬性int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);if (rc < 0) {LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "<< "__system_property_add failed";return PROP_ERROR_SET_FAILED;}}// 處理以“persist.”開頭的屬性名if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {write_persistent_property(name.c_str(), value.c_str());}property_changed(name, value);return PROP_SUCCESS; }綜上所述,property_set主要是處理普通屬性。先判斷屬性名是否合法,屬性名長度是否過長?在滿足一系列條件判斷后,再到存儲空間中去查找是否有該屬性;如果有,且是以“.ro”開頭的,表示只讀,不能修改,直接return,如果有,但不是以“.ro”開頭,則去update;如果存儲空間中沒有該屬性,則添加該屬性,并賦值。最后還調用了write_persistent_property方法處理了以“persist.”開頭的屬性名。需要注意的是persist用于持久保存某些屬性值,但會帶來額外的IO操作。
如果是以“ctl.”開頭,則表示控制消息,控制消息主要用來執行一些命令,例如:
-
查看開機動畫(setprop ctl.start bootanim)
-
關閉開機動畫(setprop ctl.stop bootanim)
-
進入recovery模式(setprop ctl.start pre-recovery)
5. 進程信號處理
在/system/core/init/init.cpp的main方法中,通過調用signal_handler_init方法來初始化信號處理進程。
5.1 signal_handler_init
深入到/system/core/init/signal_handler.cpp的signal_handler_init方法中:
void signal_handler_init() {// 創建socket pairint s[2];if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {PLOG(ERROR) << "socketpair failed";exit(1);}signal_write_fd = s[0];signal_read_fd = s[1];// 當捕獲到信號SIGCHLD,則寫入signal_write_fdstruct sigaction act;memset(&act, 0, sizeof(act));act.sa_handler = SIGCHLD_handler;//SA_NOCLDSTOP使init進程只有在其子進程終止時才會收到SIGCHLD信號act.sa_flags = SA_NOCLDSTOP;sigaction(SIGCHLD, &act, 0);//進入waitpid來處理子進程是否退出的情況ServiceManager::GetInstance().ReapAnyOutstandingChildren();//詳情見5.2//調用epoll_ctl方法來注冊epoll的回調函數register_epoll_handler(signal_read_fd, handle_signal);//詳情見5.3 }init進程是所有用戶空間進程的父進程,當子進程終止時會產生SIGCHLD信號,此時init進程會調用信號安裝函數sigaction,然后將SIGCHLD作為參數傳遞給sigaction結構體,從而完成信號處理的過程。
其中,SIGCHLD_handler是寫入數據的函數:
static void SIGCHLD_handler(int) {//向signal_write_fd寫入1,直到成功為止if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {PLOG(ERROR) << "write(signal_write_fd) failed";} }handle_signal是讀取數據的函數:
static void handle_signal() {// Clear outstanding requests.char buf[32];//讀取signal_read_fd中的數據,并放入bufread(signal_read_fd, buf, sizeof(buf));ServiceManager::GetInstance().ReapAnyOutstandingChildren(); }5.2 ServiceManager::GetInstance().ReapAnyOutstandingChildren
下面來看看處理子進程是否退出的流程:
深入到/system/core/init/service.cpp的ServiceManager::ReapAnyOutstandingChildren中:
void ServiceManager::ReapAnyOutstandingChildren() {while (ReapOneProcess()) {} }繼續深入到ReapOneProcess方法中:
bool ServiceManager::ReapOneProcess() {int status;//等待任意子進程,如果子進程沒有退出則返回0,否則則返回該子進程pidpid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));if (pid == 0) {return false;} else if (pid == -1) {PLOG(ERROR) << "waitpid failed";return false;}//根據pid查找到相應的serviceService* svc = FindServiceByPid(pid);std::string name;std::string wait_string;if (svc) {name = android::base::StringPrintf("Service '%s' (pid %d)",svc->name().c_str(), pid);if (svc->flags() & SVC_EXEC) {wait_string =android::base::StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s());}} else {name = android::base::StringPrintf("Untracked pid %d", pid);}if (WIFEXITED(status)) {LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;} else if (WIFSIGNALED(status)) {LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;} else if (WIFSTOPPED(status)) {LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string;} else {LOG(INFO) << name << " state changed" << wait_string;}if (!svc) {return true;}svc->Reap();//當flags為EXEC時,重置相應的服務if (svc->flags() & SVC_EXEC) {exec_waiter_.reset();}//當flags為EXEC時,釋放相應的服務if (svc->flags() & SVC_TEMPORARY) {RemoveService(*svc);}return true; }5.3 register_epoll_handler
register_epoll_handler是用來注冊epoll的回調函數
深入到/system/core/init/init.cpp的register_epoll_handler方法中:
void register_epoll_handler(int fd, void (*fn)()) {epoll_event ev;ev.events = EPOLLIN;ev.data.ptr = reinterpret_cast<void*>(fn);//將fd的可讀事件加入到epoll_fd的監聽隊列中if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {PLOG(ERROR) << "epoll_ctl failed";} }當fd為可讀的時候,則會觸發調用(*fn)函數。
5.4 要點總結:
5.4.1 signal_handler_init的作用
主要是防止init進程的子進程成為僵尸進程,系統會在子進程暫停或停止的時候發出SIGCHLD信號,而signal_handler_init方法會對系統發出的SIGCHLD信號進行處理,這樣就防止了僵尸進程的出現。
5.4.2 什么是僵尸進程?
在Linux中,父進程是通過fork的方式創建子進程,在子進程終止的時候,如果父進程不知道子進程已經停止,那么此時就算子進程退出,在系統進程表中還會保留一些子進程的信息(如進程號、運行時間、狀態等),那么這個子進程就成為了所謂的僵尸進程。但系統進程表中的資源空間是有限的,如果被僵尸進程耗盡,系統就無法創建新的進程。
6. 解析init.rc文件
init.rc是由Android初始化語言(Android Init Language)編寫的腳本。rc文件的語法是以行尾為單位,以空格間隔,用#開始代表注釋行。rc文件主要包含了5中類型的語句,分別是:Action、Command、Option、Service和Import。init.rc文件的目錄在system/core/rootdir/init.rc中。
6.1 Action
Action類型的語句是通過觸發器觸發,格式如下:
on <trigger> [&& <trigger>] //設置觸發器<command><command> //動作觸發后要執行的命令即以on開頭的語句來決定執行相應service的時機,具體時機有如下幾個:
-
on early-init 在初始化早期觸發
-
on init 在初始化時觸發
-
on late-init 在初始化晚期觸發
-
on boot/charger 當系統啟動或充電時觸發
-
on property:= 當屬性值滿足條件時觸發
6.2 Command
列舉一些常用的命令:
-
loglevel : 設置log級別
-
start : 啟動指定的服務,如果已經啟動則跳過
-
stop : 停止正在運行的服務
-
export : 設定環境變量
-
setprop : 設置屬性值
-
write : 向文件path中寫入字符串
6.3 Service
以service開頭的是由init進程啟動,一般都是運行在init的某個子進程中。
service類型的語句,格式如下:
service <name> <pathname> [<argument>]* //<service的名><執行程序的路徑><傳遞參數><option> //option是service的修飾詞,影響什么時候、如何啟動service<option>注意:在Android8.0對init.rc文件進行拆分,每個服務對應一個rc文件。
這里以64位處理器為例,Zygote啟動腳本則在init.zygote64.rc中定義。
init.zygote64.rc的代碼如下:(目錄:system/core/rootdir/init.zygote64.rc)
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-serverclass mainpriority -20user rootgroup root readprocsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasks根據上面的Service類型語句格式大致解析下:
-
service用于通知init進程創建名為zygote進程
-
執行程序的路徑是/system/bin/app_process64
-
傳遞的參數是/system/bin --zygote --start-system-server
-
class main 是指Zygote的classname為main
init.rc中Action、Service語句都有相應的XXXParser類來解析。即Action類型會有ActionParser來進行解析,Service類型會有ServiceParser來解析。
因為后面分析Zygote,所以主要看下ServiceParser,ServiceParser的代碼目錄是:
system/core/init/service.cpp深入到service.cpp中查看:
bool ServiceParser::ParseSection(const std::vector<std::string>& args,std::string* err) {if (args.size() < 3) { //判斷service是否有name與可執行程序*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, str_args);//注釋1return true; }bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,const std::string& filename, int line,std::string* err) const {return service_ ? service_->ParseLine(args, err) : false;//注釋2 }void ServiceParser::EndSection() {if (service_) {ServiceManager::GetInstance().AddService(std::move(service_));//注釋3} }void ServiceManager::AddService(std::unique_ptr<Service> service) {Service* old_service = FindServiceByName(service->name());if (old_service) {LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";return;}services_.emplace_back(std::move(service)); }注釋說明:
-
注釋1:調用ParseSection方法,根據傳入的參數創建出一個Service參數
-
注釋2:調用ParseLineSection方法解析出Service語句中的每一個子項,并將內容添加到Service對象中
-
注釋3:解析完所有數據后,調用EndSection函數,內部調用ServiceManager中的AddService函數,將Service對象加入vector類型的Service鏈表中。
6.4 Option
option是配合service使用,是Service中的可選項。
* socket : 創建名為/dev/socket/<name>的socket* user : 設置執行服務的用戶,默認是root* group : 設置執行服務的用戶組,默認是root* onrestart : 當服務重啟時執行相應的命令* oneshot : service退出后不再重啟* disabled : 不隨class自動啟動,只有根據service名才啟動7. init啟動Zygote
這里我們同樣以64位處理器為例,由前面可知,Zygote啟動腳本在system/core/rootdir/init.zygote64.rc中:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-serverclass mainpriority -20user rootgroup root readprocsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasks從上面可知zygote的classname是main
在init.rc中有如下兩行配置:
目錄:system/core/rootdir/init.rc
on nonencryptedclass_start main //注釋1class_start late_start其中,class_start是一個COMMAND,對應的函數是do_class_start。注釋1處表明啟動那些classname為main的service,而zygote的classname是main,所以這里是用來啟動Zygote的。
深入到do_class_start函數中:
目錄:system/core/init/builtins.cpp
static int do_class_start(const std::vector<std::string>& args) {ServiceManager::GetInstance().ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });return 0; }do_class_start()函數中會遍歷前面的Vector類型的Service鏈表,找到classname為main的Zygote,并調用system/core/init/service.cpp中的StartIfNotDisabled()函數:
bool Service::StartIfNotDisabled() {if (!(flags_ & SVC_DISABLED)) { //注釋1return Start();} else {flags_ |= SVC_DISABLED_START;}return true; }在注釋1處,如果Service中沒有在其對應的rc文件中設置disabled選項,則會調用Start函數啟動該Service,因為Zygote對應的init.zygote64.rc中沒有設置disable選項,所以直接調用Start函數啟動該Service
bool Service::Start() {// 如果Service已經運行,則直接returnif (flags_ & SVC_RUNNING) {return false;}...省略...// 判斷需要啟動的Service的對應的執行文件是否存在,不存在則不啟動該Servicestruct stat sb;if (stat(args_[0].c_str(), &sb) == -1) {PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";flags_ |= SVC_DISABLED;return false;}...省略...// 如果子進程沒有啟動,則調用fork函數創建子進程,并返回pid的值pid_t pid = -1;if (namespace_flags_) {pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);} else {pid = fork();}// 如果pid=0,則表示當前代碼邏輯在子進程中運行if (pid == 0) {umask(077);...省略...//調用execve函數,啟動Service子進程if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) { //注釋1PLOG(ERROR) << "cannot execve('" << strs[0] << "')";}_exit(127);}...省略...NotifyStateChange("running");return true; }綜上所述:
-
首先判斷Service是否已經啟動,如果運行了,則直接返回。
-
如果子進程沒有啟動,就調用fork函數創建子進程,并返回pid的值
-
如果返回pid的值是0,則表示當前代碼邏輯在子進程中運行
-
注釋1處在子進程中調用execve函數,Service子進程就會被啟動,并進入到該Service的main函數中。
如果該Service是Zygote,從前面的system/core/rootdir/init.zygote64.rc文件中我們可以知道Zygote執行程序的路徑是/system/bin/app_process64,對應的文件是app_main.cpp,這樣就會進入app_main.cpp的main函數中,即Zygote的main函數中:
代碼路徑:frameworks/base/cmds/app_process/app_main.cpp
main函數如下:
int main(int argc, char* const argv[]) {...省略...if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote); //注釋1} 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.");} }在注釋1處調用runtime的start函數啟動Zygote,到此Zygote進程就啟動了。
8. init進程啟動總結
init進程是Linux系統中用戶空間的第一個進程,主要做了如下工作:
-
創建和掛載啟動所需要的文件目錄
-
創建一塊共享內存,初始化和啟動屬性服務
-
解析各個rc文件,啟動相應屬性服務進程(如:Zygote)
-
初始化epoll,依次設置signal、property、keychord這三個fd可讀時相對應的回調函數
-
子進程信號處理,防止系統中出現僵尸進程
非常感謝您的耐心閱讀,希望我的文章對您有幫助。歡迎點評、轉發或分享給您的朋友或技術群。
總結
以上是生活随笔為你收集整理的从源码角度看Android系统init进程启动过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《18成长,19蜕变》
- 下一篇: 从源码角度看Android系统Zygot