Android系统启动流程--zygote进程的启动流程
在上一篇init进程启动流程中已经提到,在init中会解析一个init.rc文件,解析后会执行其中的命令来启动zygote进程、serviceManager进程等,下面我们来看一下:
//文件路径:system/core/init/init.cppstatic void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {//创建解析器Parser parser = CreateParser(action_manager, service_list);std::string bootscript = GetProperty("ro.boot.init_rc", "");if (bootscript.empty()) {//解析init.rc ,这个是手机设备上的路径,和源码中system/core/rootdir/init.rc是一个文件parser.ParseConfig("/system/etc/init/hw/init.rc");if (!parser.ParseConfig("/system/etc/init")) {late_import_paths.emplace_back("/system/etc/init");}// late_import is available only in Q and earlier release. As we don't// have system_ext in those versions, skip late_import for system_ext.parser.ParseConfig("/system_ext/etc/init");if (!parser.ParseConfig("/product/etc/init")) {late_import_paths.emplace_back("/product/etc/init");}if (!parser.ParseConfig("/odm/etc/init")) {late_import_paths.emplace_back("/odm/etc/init");}if (!parser.ParseConfig("/vendor/etc/init")) {late_import_paths.emplace_back("/vendor/etc/init");}} else {parser.ParseConfig(bootscript);}
}
我们来看一下这个init.rc中和zygote相关的部分:
#文件路径:system/core/rootdir/init.rc 或设备上 /system/etc/init/hw/init.rc#引入子rc文件,${ro.zygote}是一个变量,取值范围zygote32、zygote64、zygote32_64、zygote64_32,会根据实际设备是32位还是64位进行选择
import /system/etc/init/hw/init.${ro.zygote}.rc# Mount filesystems and start core system services.
on late-init# Now we can start zygote for devices with file based encryption#在init启动后的一个时机,触发启动zygote进程trigger zygote-starton zygote-start && property:ro.crypto.state=unencrypted# A/B update verifier that marks a successful boot.exec_start update_verifier_nonencryptedstart statsdstart netdstart zygote #启动zygote进程,那么这个zygote是什么呢?start zygote_secondary#还记得文件开头处的import 引入的子rc文件吗?我们以init.zygote64.rc为例:
#文件路径: system/core/rootdir/init.zygote64.rc#service zygote 服务名为zygote
#对应的可执行程序路径:/system/bin/app_process64 (设备上的路径)
#-Xzygote /system/bin --zygote --start-system-server 执行app_process64时传入的参数
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-serverclass mainpriority -20user root #用户角色group root readproc reserved_disksocket zygote stream 660 root systemsocket usap_pool_primary stream 660 root system#如果意外挂掉需要重启的服务onrestart exec_background - system system -- /system/bin/vdc volume abort_fuseonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasks
和上一篇中的init一样,zygote进程也具化到了设备中的执行程序,即app_process,我们来看下他是由那个文件编译出来的。
//文件路径 frameworks/base/cmds/app_process/Android.bpcc_binary {name: "app_process",srcs: ["app_main.cpp"], //是同目录的app_main.cpp...
}
至于怎么去找这么文件的路径目前没有发现太好的办法,只能是多熟悉每一层的bp编译脚本+关键字搜索。
打开app_main.cpp文件:
//文件路径:framesworks/base/cmds/app_process/app_main.cpp//argv :-Xzygote /system/bin --zygote --start-system-server
int main(int argc, char* const argv[])
{//创建了android运行时环境AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));//根据传入的参数设置变量值bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) { //传入参数中有--zygote,所以zygote = truezygote = true;niceName = ZYGOTE_NICE_NAME;} else if (strcmp(arg, "--start-system-server") == 0) { //同理startSystemServer = truestartSystemServer = 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()) {...}else {// We're in zygote mode.maybeCreateDalvikCache();if (startSystemServer) { //上面已经赋值为true// args中又添加了这个参数args.add(String8("start-system-server"));}...// In zygote mode, pass all remaining arguments to the zygote// main() method.for (; i < argc; ++i) {args.add(String8(argv[i]));}}...if (zygote) {//启动运行时环境,这里就是有native转到到java层的入口,这也是为什么说zygote是java层进程的鼻祖runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) { //而这个分支是启动app相关的流程,可以先有个印象后续分析到应用的启动流程可能还会看到这里// 将上面的args参数数组传入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()方法,runtime是AppRuntime类型,但AppRuntime并没有start(),因为是定义在AppRuntime的父类中,找到此方法看下具体操作:
//文件路径: frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{bool primary_zygote = false;/** 'startSystemServer == true' means runtime is obsolete and not run from* init.rc anymore, so we print out the boot start event here.*///根据options参数判断是否是第一次启动zygotefor (size_t i = 0; i < options.size(); ++i) {if (options[i] == startSystemServer) {primary_zygote = true;/* track our progress through the boot sequence */const int LOG_BOOT_PROGRESS_START = 3000;LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));}}//设置android root目录环境变量const char* rootDir = getenv("ANDROID_ROOT");if (rootDir == NULL) {rootDir = "/system";if (!hasDir("/system")) {LOG_FATAL("No root directory specified, and /system does not exist.");return;}setenv("ANDROID_ROOT", rootDir, 1);}JNIEnv* env;if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { //启动虚拟机,设置虚拟机的参数默认值return;}if (startReg(env) < 0) { //注册JNI方法,系统api中涉及的jni方法都是在这里注册的ALOGE("Unable to register all android natives\n");return;}char* slashClassName = toSlashClassName(className != NULL ? className : "");jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {}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 {//jni 执行ZygoteInit.java 的main()方法,由此过渡到java层env->CallStaticVoidMethod(startClass, startMeth, strArray);}}
}
这里放一张图用于表示进程与虚拟机的关系,即每个进程都有独立的虚拟机,而虚拟机就是一块大的内存区域,这块大内存又划分为堆、线程栈、方法区、程序计数器、本地方法栈等等更小的内存块。
继续上面的流程,进入java层的com.android.internal.os.ZygoteInit的main()函数:
// 文件路径:frameworks\base\core\java\com\android\internal\os\ZygoteInit.javapublic static void main(String argv[]) {ZygoteServer zygoteServer = null;//根据传入的参数设置变量for (int i = 1; i < argv.length; i++) {if ("start-system-server".equals(argv[i])) { //由层传入,所以startSystemServer = truestartSystemServer = 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)) {zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());} else {throw new RuntimeException("Unknown command line argument: " + argv[i]);}}if (!enableLazyPreload) {bootTimingsTraceLog.traceBegin("ZygotePreload");EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());//预加载系统资源文件,应用进程都是zygote进程fork出来的,在此进行预加载就不用在应用进程中再去重复加载资源文件,提升应用启动速度preload(bootTimingsTraceLog);EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());bootTimingsTraceLog.traceEnd(); // ZygotePreload}...zygoteServer = new ZygoteServer(isPrimaryZygote);if (startSystemServer) {//启动SystemServer进程,有zygote fork而来Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);// {@code r == null} in the parent (zygote) process, and {@code r != null} in the// child (system_server) process.if (r != null) {r.run();return;}}Log.i(TAG, "Accepting command socket connections");// The select loop returns early in the child process after a fork and// loops forever in the zygote.//死循环,等待Ams发出的创建应用进程的消息caller = zygoteServer.runSelectLoop(abiList);
}
zygote进程重要事项总结:
c++层:
- 创建Android runtime
- 启动虚拟机
- 注册JNI方法
- 通过JNI调用ZygoteInit.java的main(),转入到java层
java层:
- 预加载一些系统资源,如一些java类,resources文件,Graphics驱动、hal层的资源、动态库相关的操作等等
- 创建zygoteServer 服务端socket,等待AMS发出的创建应用进程的消息。
- fork 出systemServer进程
相关文章:

Android系统启动流程--zygote进程的启动流程
在上一篇init进程启动流程中已经提到,在init中会解析一个init.rc文件,解析后会执行其中的命令来启动zygote进程、serviceManager进程等,下面我们来看一下: //文件路径:system/core/init/init.cppstatic void LoadBoot…...

C++程序设计——异常
一、C异常概念 异常处理是一种处理错误的方式,当一个函数发现自己无法处理的错误时,就可以抛出异常,让函数的直接或间接的调用者处理这个错误。 (1)throw:当问题出现时,程序会通过throw关键字抛…...

2022年第十三届蓝桥杯web开发—东奥大抽奖【题目、附官方解答】
冬奥大抽奖 介绍 蓝桥云课庆冬奥需要举行一次抽奖活动,我们一起做一个页面提供给云课冬奥抽奖活动使用。 准备 开始答题前,需要先打开本题的项目代码文件夹,目录结构如下: ├── css │ └── style.css ├── effect.g…...

一份两年前一个月的工作经历没写在简历上,背调前主动坦白,却被背调公司亮了红灯,到手的offer没了!...
只因为简历上漏写了一份一个月的工作,就被亮了背调红灯,这公平吗?一位网友就被狠狠坑了一把,来看下他的遭遇:他有一份两年前、时长一个月的工作经历没写在简历上,背调前主动和背调公司还有招聘方hr都说了这…...

C++游戏分析与破解方法介绍
1、C游戏简介 目前手机游戏直接用C开发的已经不多,使用C开发的多是早期的基于cocos2dx的游戏,因此我们这里就以cocos2d-x为例讲解C游戏的分析与破解方法。 Cocos2d-x是一个移动端游戏开发框架,可以使用C或者lua进行开发,也可以混…...

食堂总是拥挤不堪?解决用餐拥挤,教你一招
随着近几年科技的快速发展,行业里出现了很多新的名词,比如智慧社区、智慧旅游、智慧建筑,那么智慧食堂是什么呢?它又是如何实现全自助、全智能消费? 在先进的智能技术以及市场需求带动下,智慧食堂经历了由传…...
ubuntu系统安装时 MBR和GPT的区别
主引导记录(Master Boot Record , MBR)是指一个存储设备的开头 512 字节。它包含操作系统的引导器和存储设备的分区表。 全局唯一标识分区表(GUID Partition Table,缩写:GPT)是一个实体硬盘…...

我在windows10下,使用msys64 mingw64终端
系列文章目录 文章目录系列文章目录前言一、MSYS2是什么?前言 msys2官网 MSYS2 (Minimal SYStem 2) 是一个MSYS的独立改写版本,主要用于 shell 命令行开发环境。 同时它也是一个在Cygwin (POSIX 兼容性层)…...
个人2023FALL CS申请总结(PhD/MPhil/保研夏令营)
个人2023FALL CS申请总结(PhD/MPhil/保研夏令营)写在最前个人BG及申请情况个人BG申请情况MPhilPhD收获一句话总结:心态爆炸没用,脸皮够厚够勇就行 写在最前 真是一场恶战。有几天,我每天早上都海投几封套瓷邮件&…...

【优化算法】使用遗传算法优化MLP神经网络参数(TensorFlow2)
文章目录任务查看当前的准确率情况使用遗传算法进行优化完整代码任务 使用启发式优化算法遗传算法对多层感知机中中间层神经个数进行优化,以提高模型的准确率。 待优化的模型: 基于TensorFlow2实现的Mnist手写数字识别多层感知机MLP # MLP手写数字识别…...

CAM类激活映射 |神经网络可视化 | 热力图
文章目录前言:安装库:分类案例--ResNet50分割案例AttributeError: ‘tuple‘ object has no attribute ‘cpu‘RuntimeError: grad can be implicitly created only for scalar outputsTypeError: cant convert cuda:0 device type tensor to numpy. Use…...

RecyclerView+BaseRecyclerViewAdapterHelper显示不全只显示第一行item的解决问题
RecyclerViewBaseRecyclerViewAdapterHelper显示不全只显示第一行item,我懵了…,我不说多,直接说吧 先看一下适配器代码中的convert()方法: class MineRadioAdapter(layoutResId: Int R.layout.item_my_live) :BaseQuickAdapte…...

解决后端无法对前端的ajax请求重定向
本章目录: 问题描述 AJAX请求后端直接重定向失败解决方案 后端拦截请为响应头添加重定向标志后端拦截器为响应头添加重定向路径前端响应拦截器获取响应头数据,并通过location.href url 完成页面跳转一、问题描述 本来想在拦截器里设置未登录用户访问指…...

【Python】1分钟就能制作精美的框架图?太棒啦
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、准备二、基本使用与例子1.初始化与导出2.节点类型3.集群块4.自定义线的颜色与属性总结前言 Diagrams 是一个基于Python绘制云系统架构的模块,它能…...
淘宝必备的补单技巧及注意事项!
补单,是优化善后的s单。单只是模拟用户的购物习惯,而补单同时还要模拟整个店铺的综合数据,包括点击率、转化率等等,补到略高于同行、竞品的平均数据时,淘宝会判断为买家比较喜欢你的商品,从而给你更多推荐机…...

【实用篇】SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud分布式
文章目录一、服务拆分1.1 服务拆分Demo1.2 微服务远程调用二、Eureka2.1 Eureka原理2.2 Eureka-server服务搭建2.3 eureka-client服务注册2.4 eureka-client服务复制2.5 eureka服务发现三、Ribbon负载均衡3.1 负载均衡原理3.2 负载均衡策略3.3 自定义负载均衡策略3.4 饥饿加载与…...

私人飞机、公务机包机会成为富豪圈的主流出行方式吗?
从炫耀性消费到按需使用,私人飞机的消费群体正在被拓宽,但离“成为主流”还有一段距离。“时间就是金钱”为有钱人消费私人飞机提供合理动机,而这群高净值人群的数量增长则成为撑起市场基本面。据相关数据显示,2018年全球超级富豪…...

Oracle组织架构
组织架构 (一)业务组(BG) (二)法律实体(LE) (三)业务实体(OU) (四)库存组织(INV) …...
最小公倍数
目录 最小公倍数 程序设计 程序分析 最小公倍数 【问题描述】给定两个正整数,计算这两个数的最小公倍数。 【输入形式】输入包含多组测试数据,每组只有一行,包括两个不大于1000的正整数. 【输出形式】 对于每个测试用例,给出这两个数的最小公倍数,每个实例输出一行。…...

二叉树的后序遍历(力扣145)
目录 题目描述: 解法一:递归法 解法二:迭代法 解法三:Morris遍历 二叉树的后序遍历 题目描述: 给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。 示例 1: 输入:root …...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...

springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...

Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...

【记录坑点问题】IDEA运行:maven-resources-production:XX: OOM: Java heap space
问题:IDEA出现maven-resources-production:operation-service: java.lang.OutOfMemoryError: Java heap space 解决方案:将编译的堆内存增加一点 位置:设置setting-》构建菜单build-》编译器Complier...

【Zephyr 系列 16】构建 BLE + LoRa 协同通信系统:网关转发与混合调度实战
🧠关键词:Zephyr、BLE、LoRa、混合通信、事件驱动、网关中继、低功耗调度 📌面向读者:希望将 BLE 和 LoRa 结合应用于资产追踪、环境监测、远程数据采集等场景的开发者 📊篇幅预计:5300+ 字 🧭 背景与需求 在许多 IoT 项目中,单一通信方式往往难以兼顾近场数据采集…...
【系统架构设计师-2025上半年真题】综合知识-参考答案及部分详解(回忆版)
更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7题】【第8题】【第9题】【第10题】【第11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20~21题】【第…...