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.mzph.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;组成。…