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 …...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...
