模拟Android系统Zygote启动流程

news2024/10/6 2:21:43

版权声明:本文为梦想全栈程序猿原创文章,转载请附上原文出处链接和本声明

前言:

        转眼时间过去了10年了,回顾整个10年的工作历程,做了3年的手机,4年左右的Android指纹相关的工作,3年左右的跟传感器相关的工作,其实,我个人还是想了解Android底层的相关知识,目前对于啥都了解一点,都不太深入的,让我很焦虑,记录自己学习的相关知识,这个系列的文章思路是抽丝剥茧,记录Android相关知识的关键部分,目前是从Android8.0开始。

一.前置学习:了解Android的启动流程

文笔不好,只是记录下来给自己回顾,或者分享给志同道合的人一起交流跟学习;学习本文章,你需要了解Android基本的启动流程,在这里不讲,需要自行去查看相关的资料

二.Android如何进入Zygote的java代码

这个是Framework的模拟相关代码,基本上是一比一还原

通过相关Android启动流程的相关知识,可以清楚知道,系统在执行执行并解析init.rc的过程中,启动了路径为/system/bin/app_process的可执行文件;我们知道,在Linux中,死的可执行文件称为程序,活的可执行文件称为进程;可执行文件的程序入口是main函数(这个不是很严谨)

app_process.cpp文件

int main(int argc, char* const argv[])
{

    char* argBlockStart = "test";
    size_t argBlockLength = 5;

    AppRuntime runtime(argBlockStart, argBlockLength);
    runtime.start("com.huahua.moon.android.ZygoteInit", true);

    return 0;
}

所以根据目前的main函数,需要去看下runtime.start方法,下面是app_process.cpp的整个文件内容

#include "AndroidRuntime.h"

#include <stdlib.h>

#include <stdio.h>
#include <unistd.h>

class AppRuntime : public AndroidRuntime
{
public:
    AppRuntime(char* argBlockStart, size_t argBlockLength)
        : AndroidRuntime(argBlockStart, argBlockLength)
    {

    }

};

int main(int argc, char* const argv[])
{

    char* argBlockStart = "test";
    size_t argBlockLength = 5;

    AppRuntime runtime(argBlockStart, argBlockLength);
    runtime.start("com.huahua.moon.android.ZygoteInit", true);

    return 0;
}

可以看到,runtime是AppRuntime类的对象,这个类继承自AndroidRuntime类,AppRuntime类中没有看到start函数的相关声明跟定义,所以,这个肯定是在其父类AndroidRuntime中声明跟定义的所以需要看AppRuntime跟AndroidRuntime相关的类;

这个是AndroidRuntime.h文件

#include <stddef.h>
#include <stdlib.h>

#include <jni.h>

#include <vector>

using namespace std;
using std::vector;

class AndroidRuntime
{
public:
    AndroidRuntime(char* argBlockStart, size_t argBlockSize);
    virtual ~AndroidRuntime();

    void addOption(const char* optionString, void* extra_info = NULL);

    void start(const char* className, bool zygote);

    void exit(int mode);

    static AndroidRuntime* getRuntime();

    static char* toSlashClassName(const char* className);

private:
    int startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote);

    vector<JavaVMOption> mOptions;
    char* const mArgBlockStart;
    const size_t mArgBlockLength;
    static JavaVM* mJavaVM;
};

这个是AndroidRuntime.cpp部分文件

void AndroidRuntime::start(const char* className, bool zygote)
{
    printf("AndroidRuntime start className = %s, zygote = %d \n", className, zygote);

     JNIEnv* env;
     if (startVm(&mJavaVM, &env, zygote) != 0)
     {
        return;
     }

     jstring classNameStr = env->NewStringUTF(className);
     char* slashClassName = toSlashClassName(className);

     printf("JavaVM slashClassName '%s'\n", slashClassName);
     jclass startClass = env->FindClass(slashClassName);
     if (startClass == NULL)
     {
        printf("JavaVM unable to locate class '%s'\n", slashClassName);
     }
     else
     {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
        if (startMeth != NULL)
        {
            jobjectArray strArray;
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
     }
}

所以上面app_process.cpp的main函数中,最终调用的是AndroidRuntime.cpp中的start函数,传递的参数className = ("com.huahua.moon.android.ZygoteInit"  zygote = true;因为我这篇文章是模拟,只是概述关键点,所有有其他的很多东西没有提及到;这里跟我上一次的文章是有联系的,具体也可以参考我上篇博客linux运行可执行文件,通过c语言调用java的main方法

上面的代码,主要是一下几点:

  • 启动JVM虚拟机
  • 转换class Name的格式
  • 找到对应的class
  • 获取对应class的main函数
  • 执行main函数

这里有一个细节,就是我们传递的进来的class名是"com.huahua.moon.android.ZygoteInit",这个名字是不符合JAVA相关的JNI规范的,需要修改成"com/huahua/moon/android/ZygoteInit"这种名字

startVm函数是启动java虚拟机

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
    JavaVMInitArgs initArgs;

#if 0
    addOption("-Djava.class.path=.");

    initArgs.version = JNI_VERSION_1_4;
    //initArgs.options = mOptions.editArray();
    //initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;
#endif


    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=.";

    initArgs.version = JNI_VERSION_1_4;
    initArgs.nOptions = 1;
    initArgs.options = options;

    if (JNI_CreateJavaVM(pJavaVM, (void **)pEnv, &initArgs) < 0)
    {
        printf("JNI_CreateJavaVM failed \n");
        return -1;
    }
    else
    {
        printf("JNI_CreateJavaVM create success  \n");
    }

    return 0;
}

toSlashClassName函数是把"com.huahua.moon.android.ZygoteInit"转化成"com/huahua/moon/android/ZygoteInit"这种格式

char* AndroidRuntime::toSlashClassName(const char* className)
{
    char* result = strdup(className);
    for (char* cp = result; *cp != '\0'; cp++) {
        if (*cp == '.') {
            *cp = '/';
        }
    }
    return result;
}

三.模拟结果

通过执行app_process 可执行文件, 输出了java 代码的main函数中的系统输出,证明了Android 进入了Zygote的java代码

ZygoteInit.java 的代码如下图所示

三.注意问题

模拟代码是一个稍微完整的工程代码,本篇文章只是包含了其中的一部分

四.如何编译测试

1.用本工程的java代码编译出class文件,使用java命令或者intellij idea 工具编译都行

2.使用本工程的cpp部分代码,编译出app_process 可执行文件,目前本工程的构建工具是Cmake

3.拷贝到out目前, 请注意目录结构

五.思考问题

真的只有面向对象语言有封装的特性吗? 

此工程中的cpp代码从封装性,跟C语言的代码对比而言.(ps:在这个头文件中竟然能看到类的相关私有数据成员)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1595591.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java快速入门系列-9(Spring框架与Spring Boot —— 深度探索及实践指南)

第九章:Spring框架与Spring Boot —— 深度探索及实践指南 9.1 Spring框架概述9.2 Spring IoC容器9.3 Spring AOP9.4 Spring MVC9.5 Spring Data JPA/Hibernate9.6 Spring Boot快速入门与核心特性9.7 Spring Boot的自动配置与启动流程详解9.8 创建RESTful服务与数据库交互实践…

数字化仪:为何成为示波器的理想替代品?——PCIe8910M

在现代科技领域&#xff0c;数字化仪逐渐成为示波器的理想替代品。数字化仪具备诸多特点&#xff0c;使其在多个应用场景下表现出色&#xff0c;逐渐取代传统的示波器。本期文章将探讨数字化仪相对于示波器的优势&#xff0c;以及其哪些特点使其成为示波器的理想替代品。 简介…

AI时代的计算核心,你了解多少?

CPU是中央处理单元&#xff0c;那么GPU是什么呢&#xff1f; CPU的作用是计算机的运算核心和控制核心&#xff0c;GPU作用是什么呢&#xff1f; CPU的大小叫着内存大小&#xff0c;那GPU的大小叫什么呢&#xff1f; 下面我们来聊聊GPU 说起GPU&#xff0c;先来看看我们更为…

将Ubuntu18.04默认的python3.6升级到python3.8

1、查看现有的 python3 版本 python3 --version 2、安装 python3.8 sudo apt install python3.8 3、将 python3.6 和 3.8 添加到 update-alternatives sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1 sudo update-alternatives --insta…

虚拟资源素材会员交易平台网站源码 带完整源码及教程

今天给大家分享一个虚拟资源素材下载站源码系统&#xff0c;这是一款大家非常需要的虚拟资源下载站源码系统&#xff0c;拥有强大的会员功能&#xff0c;可以单独售卖资源&#xff0c;或者开通会员进行打折购买&#xff0c;或者超级VIP免费下载等等&#xff0c;支持按照时间开通…

为什么看到这么多人不推荐C++?

前几天逛知乎的时候&#xff0c;看到一个问题&#xff1a; 看到这个问题我倒是想吐槽几句了。 C一直没找到自己的定位&#xff01; C语言&#xff1a;我是搞系统编程开发的&#xff0c;操作系统、数据库、编译器、网络协议栈全是我写的。 PHP&#xff1a;我是搞后端业务开发…

不花一分钱,四大方法教你免费申请SSL证书

在数字化时代&#xff0c;数据安全与隐私保护的重要性日益凸显。为了确保在线信息传输的机密性和完整性&#xff0c;数字证书&#xff0c;尤其是SSL/TLS证书扮演着至关重要的角色。为个人及企业用户提供了经济、高效的加密解决方案。随着市场对SSL证书的逐渐重视&#xff0c;免…

Vue3 笔记

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n 经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 截止2023年10月&#xff0c;最…

idea 中运行spring boot 项目报 Command line is too long的解决办法。

Command line is too long 在这里选择edit configures 选择shrten command line , 选择 jar manifest 运行即可。

【Docker】docker原理及使用-1

Docker目录 1️⃣概念2️⃣使用容器的好处2️⃣docker和普通软件启动方式的区别2️⃣docker和传统虚拟机的区别 1️⃣下载安装2️⃣安装步骤 1️⃣必须要掌握的核心概念1️⃣命令2️⃣例子2️⃣练习题目2️⃣进入一下python环境(简洁) 1️⃣解释一下 redis1️⃣docker底层隔离机…

Spring框架中的单例bean是线程安全的吗?

无状态bean&#xff1a; 无状态的Bean的行为不受其内部状态的影响&#xff0c;每次调用都是基于传入的参数进行计算&#xff0c;而不依赖于任何之前的状态。 (例如上面例子&#xff1a;userService是不能修改的&#xff0c;是无状态的bean) 因此&#xff1a; Spring框架中的…

【位运算】Leetcode 丢失的数字

题目解析 268. 丢失的数字 本题的意思就是数组的长度为n&#xff0c;在[0,n]区间中寻找缺失的一个数字 算法讲解 直观思路&#xff1a;排序 Hash&#xff0c;顺序查找缺失的数字 优化&#xff1a;使用异或&#xff0c;首先将[0,n]之间所有数字异或在一起&#xff0c;然后将…

django 模板js文件为什么最后引入

<!-- 引入Bootstrap JS --> <script src"https://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/js/bootstrap.bundle.min.js"></script> 为什么最后引入例子 <!-- templates/inspection_records.html --><!DOCTYPE html> <html lang…

ROS2从入门到精通1-3:详解ROS2动作通信机制与自定义动作

目录 0 专栏介绍1 动作通信模型2 动作模型实现(C)3 动作模型实现(Python)4 自定义动作 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 &#x1f680;详情&a…

【论文阅读——SplitFed: When Federated Learning Meets Split Learning】

级别CCFA 1.摘要 联邦学习&#xff08;FL&#xff09;和分割学习&#xff08;SL&#xff09;是两种流行的分布式机器学习方法。两者都采用了模型对数据的场景&#xff1b;客户端在不共享原始数据的情况下训练和测试机器学习模型。由于机器学习模型的架构在客户端和服务器之间…

vue.config.js跨域问题解决

讲解视频 问题背景 目标地址&#xff1a; 而当前项目启动是http&#xff0c;协议名不同&#xff0c;所以跨域了 解决步骤和解答 1. 新建vue.config.js文件 2. 添加如下代码&#xff1a; 一般目标路径target写 域名 就可以了 但其实&#xff0c;写路径也可以&#xff0c;…

移动应用安全合规动态:网信办、金管局发文强调数据安全;3月个人信息违规抽查结果出炉!(第五期)

一、监管部门动向&#xff1a;国家互联网信息办公室公布《促进和规范数据跨境流动规定》; 工信部发布《关于网络安全保险典型服务方案目录的公示》 二、安全新闻&#xff1a;恶意软件警报&#xff01;黑客利用软件即服务攻击印度安卓用户&#xff1b;Cerberus银行恶意软件的虚…

Linux内核常见的丢包场景有哪些

目录 摘要 1 收发包处理流程 2 硬件网卡相关 2.1 ring buffer满 2.2 利用 ntuple 保证关键业务 3 arp丢包 3.1 neighbor table overflow 3.2 unresolved drops 4 conntrack丢包&#xff1a;nf_conntrack: table full 5 udp接收buffer满 6 丢包定位 6.1 dropwatch 查看丢包 6.2…

ERROR 1052 (23000): Column ‘deptno‘ in field list is ambiguous

错误原因&#xff1a; 这个错误通常是在多表查询中&#xff0c;因为你的SQL查询中包含了多个表&#xff0c;并且这些表中都有一个名为deptno的列。这会导致数据库无法确定你要引用哪个表中的 deptno列&#xff0c;从而产生歧义。 解决方法&#xff1a; 为了解决这个问…

【数据结构(六)】队列

❣博主主页: 33的博客❣ ▶️文章专栏分类:数据结构◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你学更多数据结构知识 目录 1.前言2.概念3.队列的使用4.循环队列5.双端队列6.经典习题6.1队列实现栈6.2栈实现队…