Android系統啟動系列----init進程

Android系統啟動系列

  1. Android系統啟動系列----init進程
  2. Android系統啟動系列----Zygote進程

引言

在開發app的過程中,是不是會有疑問:

  • java程序的運行不是從main方法開始么,怎么app入口是Application的onCreate方法?
  • 那java的運行環境虛擬機Dalvik VM和ART又是什么時候創建的?又是由誰創建的?
  • 安卓是Linux內核,那內核創建后系統又做了什么初始化了整個安卓環境?
  • 當我們的手機或者安卓系統設備按下電源按鍵的時候,系統都做什么?

當按下電源的那一刻都發生了啥:

今天的分析都是基于Android 6.0系統的分析。


第一步:啟動電源

當電源按下,引導芯片代碼開始從預定義的地方(固化在ROM)開始執行。加載引導程序到RAM,然后執行。

第二步:執行引導程序(Boot Loader)

通常在運行Android系統之前會先執行Boot Loader引導程序,它不屬于Android系統,常見的引導程序有:redboot、uboot、qi bootloader等等。或者自行開發引導程序,它是針對特定主板和芯片的,OEM制造廠商或者運營商在加鎖的時候就對這個引導程序做修改,比如魅族就是修改了引導程序,所以刷不了機。

第三步:內核

Android內核與桌面linux內核啟動的方式差不多。內核啟動時,設置緩存、被保護存儲器、計劃列表,加載驅動。當內核完成系統設置,它首先在系統文件中尋找”init”文件,然后啟動root進程或者系統的第一個進程。

第四步:執行init進程

init進程是Android系統啟動的第一個用戶空間進程,init進程主要做兩個事情。第一:掛載目錄,如:掛載了/sys /dev /proc 等目錄。第二:解析執行init.rc腳本文件。

系統編譯,刷入手機后,init的進程保存在/system/core/bin目錄中,對應程序的源代碼入口是/system/core/init/init.cpp。

int main(int argc, char** argv) {if (!is_first_stage) {// Indicate that booting is in progress to background fw loaders, etc.close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));// 初始化屬性服務property_init();// If arguments are passed both on the command line and in DT,// properties set in DT always have priority over the command-line ones.process_kernel_dt();process_kernel_cmdline();// Propogate the kernel variables to internal variables// used by init as well as the current required properties.export_kernel_boot_props();}........//  開始屬性服務start_property_service();// 初始化“init.rc”配置文件解析器init_parse_config_file("/init.rc");action_for_each_trigger("early-init", action_add_queue_tail);........
}
復制代碼

主要看init.rc腳本文件的解析,在說解析前,先來了解下配置腳本的內容,這是一個內建的腳本語言也叫Android初始化語言,有自己的語法結構,大概介紹下: Android初始化語言由四大類型的聲明組成,即Actions(動作)、Commands(命令)、Services(服務)、以及Options(選項)。 Action(動作):動作是以命令流程命名的,有一個觸發器決定動作是否發生。

on early-init# Set init and its forked children's oom_adj.write /proc/1/oom_score_adj -1000# Set the security context of /adb_keys if present.restorecon /adb_keysstart ueventdon initsysclktz 0# Backward compatibility.symlink /system/etc /etcsymlink /sys/kernel/debug /d# Link /vendor to /system/vendor for devices without a vendor partition.symlink /system/vendor /vendor# Create cgroup mount point for cpu accountingmkdir /acctmount cgroup none /acct cpuacctmkdir /acct/uid
復制代碼

以上腳本中,on early-init、on init就是 Action類型的語句,語法格式為:

on <trigger> [&& <trigger>]*     //設置觸發器  <command>  <command>      //動作觸發之后要執行的命令
復制代碼

Service(服務):服務是init進程啟動的程序、當服務退出時init進程會視情況重啟服務,語法格式為:

 service <name> <pathname> [ <argument> ]*   //<service的名字><執行程序路徑><傳遞參數>  <option>       //option是service的修飾詞,影響什么時候、如何啟動services  <option>  ... 
復制代碼

下面是默認的init.rc文件,主要的事件及其服務。

Action/Service描述
on early-init設置init進程以及它創建的子進程的優先級,設置init進程的安全環境
on init設置全局環境,為cpu accounting創建cgroup(資源控制)掛載點
on fs掛載mtd分區
on post-fs改變系統目錄的訪問權限
on post-fs-data改變/data目錄以及它的子目錄的訪問權限
on-boot基本網絡的初始化,內存管理等等
service servicemanager啟動系統管理器管理所有的本地服務,比如位置、音頻、Shared preference等等…
service zygote啟動zygote進程

通常在這個階段,我們可以在屏幕上看到“Android logo”字樣或者圖標。

我們重點來看看zygote進程相關的屬性配置,它是獨立的一個rc文件在/system/core/rootdir/init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-serverclass mainsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart mediaonrestart restart netdwritepid /dev/cpuset/foreground/tasks
復制代碼

執行zygote程序,其實是通過執行app_process程序,然后傳入xzygote等等參數實現的。先找到app_process程序的源碼所在地:/frameworks/base/cmds/app_process/app_main.cpp 直接看程序的main函數:

int main(int argc, char* const argv[])
{if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {// Older kernels don't understand PR_SET_NO_NEW_PRIVS and return// EINVAL. Don't die on such kernels.if (errno != EINVAL) {LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));return 12;}}AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// Process command line arguments// ignore argv[0]argc--;argv++;int i;for (i = 0; i < argc; i++) {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 = true;} else if (strncmp(arg, "--nice-name=", 12) == 0) {niceName.setTo(arg + 12);} else if (strncmp(arg, "--", 2) != 0) {className.setTo(arg);break;} else {--i;break;}}Vector<String8> args;if (!className.isEmpty()) {args.add(application ? String8("application") : String8("tool"));runtime.setClassNameAndArgs(className, argc - i, argv + i);} else {// We're in zygote mode.maybeCreateDalvikCache();if (startSystemServer) {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);// 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());set_process_name(niceName.string());}// 如果參數是--zygote,那么runtime.start執行zygote進程if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} 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.");return 10;}
}
復制代碼

上面就是通過app_process進程,啟動zygote進程的入口,執行啟動zygote的程序的在java層的/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java,接下來我們看看:runtime.start("com.android.internal.os.ZygoteInit", args, zygote)都干了啥。 runtime是AppRuntime類的對象,start函數在其父類AndroidRuntime中聲明和實現。AndroidRuntime是不是很熟悉了,Android的運行時,通常app異常的時候這玩意是不是總伴隨你左右。原來這玩意這么早就啟動在監控系統的一舉一動了。

/** Start the Android runtime.  This involves starting the virtual machine* and calling the "static void main(String[] args)" method in the class* named by "className".** Passes the main function two arguments, the class name and the specified* options string.*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{....../* start the virtual machine */JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (startVm(&mJavaVM, &env, zygote) != 0) {  // 1return;}onVmCreated(env);   // 2/** Register android functions.*/if (startReg(env) < 0) {ALOGE("Unable to register all android natives\n");return;}/** We want to call main() with a String array with arguments in it.* At present we have two arguments, the class name and an option string.* Create an array to hold them.*/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.*/char* slashClassName = toSlashClassName(className);jclass startClass = env->FindClass(slashClassName);  // 3if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");  // 4if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {env->CallStaticVoidMethod(startClass, startMeth, strArray);  // 5#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}......
}
復制代碼
  1. 注釋1:startVm顧名思義啟動虛擬機,在此啟動java虛擬機,當然這個是運行zygote進程的虛擬機,也就回答了文章最開始引言問題,虛擬機由app_process的AndroidRuntime創建。
  2. 注釋2:虛擬機創建后的回調處理,主要是創建一些資源。
  3. 注釋3:className就是app_process中傳入的參數“com.android.internal.os.ZygoteInit”,因為ZygoteInit是java層的,所以需要使用jni來找到ZygoteInit.class,startClass就是ZygoteInit.class
  4. 注釋4:通過jni的GetStaticMethodID函數獲取到ZygoteInit.java的靜態main方法的類似于反射的Mehod對象引用。
  5. 注釋5:最后通過JNI的CallStaticVoidMethod函數類似于java反射的invoke方法,調用了4中獲取的main方法的Method引用。 (如果對JNI不熟的可以看看JNI系列入門文章)

如上就是Zygote進程的啟動方式。


總結

  1. 手機按下電源后,加載引導程序到內存中。
  2. 執行引導程序
  3. 啟動內核,設置緩存、被保護存儲器、計劃列表,加載驅動,查找/system/core/bin中init程序文件。
  4. 啟動init程序,掛載/sys /dev /proc等等目錄,加載和解析init.rc腳本。
  5. 在加載init.rc腳本的時候,啟動app_process進程。
  6. 在app_process進程中,根據init.zygote32.rc腳本配置的參數,啟動zygote進程。
  7. 最終zygote進程的執行,即ZygoteInit.java文件main方法的執行,是由AndroidRuntime通過JNI的方式調用main方法執行的。在這之前啟動了Dalvik VM或者ART虛擬機。

Android系統啟動系列

  1. Android系統啟動系列----init進程
  2. Android系統啟動系列----Zygote進程

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/389863.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/389863.shtml
英文地址,請注明出處:http://en.pswp.cn/news/389863.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

598. 范圍求和 II

598. 范圍求和 II 給定一個初始元素全部為 0&#xff0c;大小為 m*n 的矩陣 M 以及在 M 上的一系列更新操作。 操作用二維數組表示&#xff0c;其中的每個操作用一個含有兩個正整數 a 和 b 的數組表示&#xff0c;含義是將所有符合 0 < i < a 以及 0 < j < b 的元…

mysql 數據庫優化之執行計劃(explain)簡析

數據庫優化是一個比較寬泛的概念&#xff0c;涵蓋范圍較廣。大的層面涉及分布式主從、分庫、分表等&#xff1b;小的層面包括連接池使用、復雜查詢與簡單查詢的選擇及是否在應用中做數據整合等&#xff1b;具體到sql語句執行效率則需調整相應查詢字段&#xff0c;條件字段&…

自我接納_接納預測因子

自我接納現實世界中的數據科學 (Data Science in the Real World) Students are often worried and unaware about their chances of admission to graduate school. This blog aims to help students in shortlisting universities with their profiles using ML model. The p…

距離產生美

那天下午我跟簡坐在學校操作草地上聊天 夕陽的余暉照射著我們 陽光在青草的縫隙間拉長了倒影 溫暖的晚風輕拂著簡劉海前的幾根發絲 淡淡的發香迎面撲來&#xff0c;我望著遠山上的煙囪。 對簡說&#xff1a; 我覺得我們坐得太近了。感覺相距 50cm 比較好。 簡一臉驚訝說&#x…

299. 猜數字游戲

299. 猜數字游戲 你在和朋友一起玩 猜數字&#xff08;Bulls and Cows&#xff09;游戲&#xff0c;該游戲規則如下&#xff1a; 寫出一個秘密數字&#xff0c;并請朋友猜這個數字是多少。朋友每猜測一次&#xff0c;你就會給他一個包含下述信息的提示&#xff1a; 猜測數字…

mysql數據庫中case when 的用法

場景1&#xff1a;比如說我們在數據庫存了性別的字段&#xff0c;一般都是存0 和 1 代表男和女 然后我們會得到0和1之后在java中判斷 &#xff0c;很麻煩有么有&#xff1f;其實我們完全可以在sql中判斷好之后拿來現成的。就是在sql中做判斷就ok SELECT*,CASEWHEN ly app th…

python中knn_如何在python中從頭開始構建knn

python中knnk最近鄰居 (k-Nearest Neighbors) k-Nearest Neighbors (KNN) is a supervised machine learning algorithm that can be used for either regression or classification tasks. KNN is non-parametric, which means that the algorithm does not make assumptions …

CRT配色

http://a0bd2668.wiz03.com/share/s/2wLipE0wJ4Wl28H1oC2BIvEv02vmgz3S_QjT2YHyWG2t2nng轉載于:https://blog.51cto.com/13420391/2164540

5920. 分配給商店的最多商品的最小值

5920. 分配給商店的最多商品的最小值 給你一個整數 n &#xff0c;表示有 n 間零售商店。總共有 m 種產品&#xff0c;每種產品的數目用一個下標從 0 開始的整數數組 quantities 表示&#xff0c;其中 quantities[i] 表示第 i 種商品的數目。 你需要將 所有商品 分配到零售商…

A*

轉自http://www.mamicode.com/info-detail-1534200.html康托展開X a[1]*(n-1)!a[2]*(n-2)!...a[i]*(n-i)!...a[n-1]*1!a[n]*0!其中a[i]表示在num[i1..n]中比num[i]小的數的數量逆康托展開由于&#xff1a;a[i]≤n-i, a[i]*(n-i)!≤(n-i)*(n-i)!<(n-i1)!于是我們得到&#x…

unity第三人稱射擊游戲_在游戲上第3部分完美的信息游戲

unity第三人稱射擊游戲Previous article上一篇文章 The economics literature distinguishes the quality of a game’s information (perfect vs. imperfect) from the completeness of a game’s information (complete vs. incomplete). Perfect information means that ev…

JVM(2)--一文讀懂垃圾回收

與其他語言相比&#xff0c;例如c/c&#xff0c;我們都知道&#xff0c;java虛擬機對于程序中產生的垃圾&#xff0c;虛擬機是會自動幫我們進行清除管理的&#xff0c;而像c/c這些語言平臺則需要程序員自己手動對內存進行釋放。 雖然這種自動幫我們回收垃圾的策略少了一定的靈活…

2058. 找出臨界點之間的最小和最大距離

2058. 找出臨界點之間的最小和最大距離 鏈表中的 臨界點 定義為一個 局部極大值點 或 局部極小值點 。 如果當前節點的值 嚴格大于 前一個節點和后一個節點&#xff0c;那么這個節點就是一個 局部極大值點 。 如果當前節點的值 嚴格小于 前一個節點和后一個節點&#xff0c;…

tb計算機存儲單位_如何節省數TB的云存儲

tb計算機存儲單位Whatever cloud provider a company may use, costs are always a factor that influences decision-making, and the way software is written. As a consequence, almost any approach that helps save costs is likely worth investigating.無論公司使用哪種…

nginx簡單代理配置

原文&#xff1a;https://my.oschina.net/wangnian/blog/791294 前言 Nginx ("engine x") 是一個高性能的HTTP和反向代理服務器&#xff0c;也是一個IMAP/POP3/SMTP服務器。Nginx是由Igor Sysoev為俄羅斯訪問量第二的Rambler.ru站點開發的&#xff0c;第一個公開版本…

2059. 轉化數字的最小運算數

2059. 轉化數字的最小運算數 給你一個下標從 0 開始的整數數組 nums &#xff0c;該數組由 互不相同 的數字組成。另給你兩個整數 start 和 goal 。 整數 x 的值最開始設為 start &#xff0c;你打算執行一些運算使 x 轉化為 goal 。你可以對數字 x 重復執行下述運算&#xf…

Django Rest Framework(一)

一、什么是RESTful REST與技術無關&#xff0c;代表一種軟件架構風格&#xff0c;REST是Representational State Transfer的簡稱&#xff0c;中文翻譯為“表征狀態轉移”。 REST從資源的角度審視整個網絡&#xff0c;它將分布在網絡中某個節點的資源通過URL進行標識&#xff0c…

光落在你臉上,可愛一如往常

沙沙野 https://www.ssyer.com讓作品遇見全世界圖片來自&#xff1a;沙沙野沙沙野 https://www.ssyer.com讓作品遇見全世界圖片來自&#xff1a;沙沙野沙沙野 https://www.ssyer.com讓作品遇見全世界圖片來自&#xff1a;沙沙野沙沙野 https://www.ssyer.com讓作品遇見全世界圖…

數據可視化機器學習工具在線_為什么您不能跳過學習數據可視化

數據可視化機器學習工具在線重點 (Top highlight)There’s no scarcity of posts online about ‘fancy’ data topics like data modelling and data engineering. But I’ve noticed their cousin, data visualization, barely gets the same amount of attention. Among dat…

2047. 句子中的有效單詞數

2047. 句子中的有效單詞數 句子僅由小寫字母&#xff08;‘a’ 到 ‘z’&#xff09;、數字&#xff08;‘0’ 到 ‘9’&#xff09;、連字符&#xff08;’-’&#xff09;、標點符號&#xff08;’!’、’.’ 和 ‘,’&#xff09;以及空格&#xff08;’ &#xff09;組成。…