frameworks 之Zygote

news/2024/9/11 2:48:37/ 标签: zygote

frameworks 之Zygote

  • Zygote.rc 解析
  • Zygote 启动
  • ZygoteInit.java
  • Zygote.cpp
  • Liunx fork

Zygote 中文意思为受精卵。 和其意思一样,该功能负责android系统孵化service 和 app 进程。
本文讲解Zygote的大概流程。涉及的相同的类,如下所示

  • system/core/rootdir/init.zygote32.rc
  • frameworks/base/cmds/app_process/app_main.cpp
  • frameworks/base/core/jni/AndroidRuntime.cpp
  • system/core/init/main.cpp
  • frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  • frameworks/base/core/java/com/android/internal/os/Zygote.java
  • frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

Zygote.rc 解析

启动init进程后,会解析 Zygote.rc文件。该文件位于 system/core/rootdir 文件夹下
其中第一行 zygote 表示进程名, /system/bin/app_process 表示要启动的模块名 ,–zygote --start-system-server 表示参数, class main 表示入口方法。

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

Zygote 启动

根据上面的rc文件 全局搜索 grep app_process ./ -rn
可以看到该模块名为app_process的位于 base/cmds/app_process 下。
在这里插入图片描述
跳转到该文件夹下 打开 app_main.cpp 文件,查看main 方法
main 方法前面是解析参数,并对变量 zygote, startSystemServer 设置为true, 通过 runtime.start 方法启动, start方法是在继承在 AndroidRuntime 类实现

int main(int argc, char* const argv[])
{// Parse runtime arguments.  Stop at first unrecognized option.bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;// 将变量为true++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;}}// zygote 为true 通过runtime启动,com.android.internal.os.ZygoteInit 为类名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.");}
}

通过 全局查找 grep “AndroidRuntime” ./ -rn 得到该类的位置 在 core/jni/AndroidRuntime.cpp 下
在这里插入图片描述
该方法前面大部分还是参数变量判断,关键通过 jmethodID startMeth = env->GetStaticMethodID(startClass, “main”,
“([Ljava/lang/String;)V”); 启动 对应的main方法。根据上一步传进来的参数。可以得到启动了 ZygoteInit.java 类

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{/** 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;// 省略// 加载传进来的类名,jni 加载对应的main方法char* slashClassName = toSlashClassName(className != NULL ? className : "");jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} 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 {env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}free(slashClassName);ALOGD("Shutting down VM\n");if (mJavaVM->DetachCurrentThread() != JNI_OK)ALOGW("Warning: unable to detach main thread\n");if (mJavaVM->DestroyJavaVM() != 0)ALOGW("Warning: VM did not shut down cleanly\n");
}

ZygoteInit.java

查看对应的main方法 ,main 里面主要的方法有如下
preload(bootTimingsTraceLog); 预加载了类,资源,opengl,so库等
初始化ZygoteServer
forkSystemServer 启动 SystemServer
runSelectLoop 循环等待消息

public static void main(String[] argv) {...// 初始化参数 startSystemServer 决定启动 systemServer 服务boolean startSystemServer = false;String zygoteSocketName = "zygote";String abiList = null;boolean enableLazyPreload = false;for (int i = 1; i < argv.length; i++) {if ("start-system-server".equals(argv[i])) {startSystemServer = 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]);}}...// In some configurations, we avoid preloading resources and classes eagerly.// In such cases, we will preload things prior to our first fork.if (!enableLazyPreload) {bootTimingsTraceLog.traceBegin("ZygotePreload");EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());// 加载类资源 和so preload(bootTimingsTraceLog);EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());bootTimingsTraceLog.traceEnd(); // ZygotePreload}...// 创建systemServiver 服务zygoteServer = new ZygoteServer(isPrimaryZygote);if (startSystemServer) {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.caller = zygoteServer.runSelectLoop(abiList);
}

其中 preload 里面的 preloadClasses 加载android所需的类 ,加载 preloaded-classes 文件 通过Class.forName 加载类。可通过find -name preloaded-classes 查看该文件的位置该文件通过编译时候拷贝到 system/ect目录下 。
在这里插入图片描述

static void preload(TimingsTraceLog bootTimingsTraceLog) {preloadClasses();preloadResources();nativePreloadAppProcessHALs();preloadSharedLibraries();preloadTextResources();
}private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";private static void preloadClasses() {// 加载文件InputStream is;try {is = new FileInputStream(PRELOADED_CLASSES);} catch (FileNotFoundException e) {Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");return;}...// 循环遍历 通过 Class.forName 加载类文件while ((line = br.readLine()) != null) {// Skip comments and blank lines.line = line.trim();if (line.startsWith("#") || line.equals("")) {continue;}Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);try {// Load and explicitly initialize the given class. Use// Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups// (to derive the caller's class-loader). Use true to force initialization, and// null for the boot classpath class-loader (could as well cache the// class-loader of this class in a variable).Class.forName(line, true, null);count++;} catch (ClassNotFoundException e) {if (line.contains("$$Lambda$")) {if (LOGGING_DEBUG) {missingLambdaCount++;}} else {Log.w(TAG, "Class not found for preloading: " + line);}} catch (UnsatisfiedLinkError e) {Log.w(TAG, "Problem preloading " + line + ": " + e);} catch (Throwable t) {Log.e(TAG, "Error preloading " + line + ".", t);if (t instanceof Error) {throw (Error) t;} else if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new RuntimeException(t);}}Trace.traceEnd(Trace.TRACE_TAG_DALVIK);}
}

runSelectLoop 里面是一个死循环,poll等待消息 如果没消息来 就会卡在这 ,如果第一次进来,调用 acceptCommandPeer,接着 acceptCommandPeer 又会调用 createNewConnection方法 创建 ZygoteConnection。

			..// poll等待消息 如果没消息来 就会卡在这try {pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);} catch (ErrnoException ex) {throw new RuntimeException("poll failed", ex);}...//如果第一次进来,调用 acceptCommandPeer,创建 ZygoteConnectionif (pollIndex == 0) {// Zygote server socketZygoteConnection newPeer = acceptCommandPeer(abiList);peers.add(newPeer);socketFDs.add(newPeer.getFileDescriptor());} else if (pollIndex < usapPoolEventFDIndex) {ZygoteConnection connection = peers.get(pollIndex);boolean multipleForksOK = !isUsapPoolEnabled()&& ZygoteHooks.isIndefiniteThreadSuspensionSafe();// 执行创建final Runnable command =connection.processCommand(this, multipleForksOK);}

processCommand 方法里面会调用 forkAndSpecialize 继续调用 nativeForkAndSpecialize 创建进程,而 nativeForkAndSpecialize又会调用 ForkCommon 创建。

if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote|| !multipleOK || peer.getUid() != Process.SYSTEM_UID) {// Continue using old code for now. TODO: Handle these cases in the other path.// 创建进程pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,parsedArgs.mBindMountAppStorageDirs);try {if (pid == 0) {// in childzygoteServer.setForkChild();zygoteServer.closeServerSocket();IoUtils.closeQuietly(serverPipeFd);serverPipeFd = null;return handleChildProc(parsedArgs, childPipeFd,parsedArgs.mStartChildZygote);} else {// In the parent. A pid < 0 indicates a failure and will be handled in// handleParentProc.IoUtils.closeQuietly(childPipeFd);childPipeFd = null;handleParentProc(pid, serverPipeFd);return null;}} finally {IoUtils.closeQuietly(childPipeFd);IoUtils.closeQuietly(serverPipeFd);}}

Zygote.cpp

forkSystemServer 会调用 Zygote.forkSystemServer 方法,而forkSystemServer 又会调用
nativeForkSystemServer 方法, 最终调用到 Zygote.cpp 里面的方法。直接查找 Zygote.cpp 可以看到该文件位于 frameworks/base/core/jni 。

			/* Request to fork the system server process */pid = Zygote.forkSystemServer(parsedArgs.mUid, parsedArgs.mGid,parsedArgs.mGids,parsedArgs.mRuntimeFlags,null,parsedArgs.mPermittedCapabilities,parsedArgs.mEffectiveCapabilities);
	static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {ZygoteHooks.preFork();int pid = nativeForkSystemServer(uid, gid, gids, runtimeFlags, rlimits,permittedCapabilities, effectiveCapabilities);// Set the Java Language thread priority to the default value for new apps.Thread.currentThread().setPriority(Thread.NORM_PRIORITY);ZygoteHooks.postForkCommon();return pid;}

看到关键语句pid 可以看出该方法是fork 出进程id, 调用的是liunx 自带fork方法 孵化出进程,当返回的id为0时候 代表是新进程,可以看到会调用 ForkCommon 方法。

pid_t pid = zygote::ForkCommon(env, true,fds_to_close,fds_to_ignore,true);if (pid == 0) {// System server prcoess does not need data isolation so no need to// know pkg_data_info_list.SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities,effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,false, nullptr, nullptr, /* is_top_app= */ false,/* pkg_data_info_list */ nullptr,/* allowlisted_data_info_list */ nullptr, false, false);}

查看 ForkCommon 方法。里面调用了 Fork方法 创建进程

// 创建进程
pid_t pid = fork();if (pid == 0) {if (is_priority_fork) {setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);} else {setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);}// The child process.PreApplicationInit();// Clean up any descriptors which must be closed immediatelyDetachDescriptors(env, fds_to_close, fail_fn);// Invalidate the entries in the USAP table.ClearUsapTable();// Re-open all remaining open file descriptors so that they aren't shared// with the zygote across a fork.gOpenFdTable->ReopenOrDetach(fail_fn);// Turn fdsan back on.android_fdsan_set_error_level(fdsan_error_level);// Reset the fd to the unsolicited zygote socketgSystemServerSocketFd = -1;} else {ALOGD("Forked child process %d", pid);}

Liunx fork

fork函数是 Liunx ,fork() 返回的pid pid等于0 表示三fork新进程执行 不等于0 原来的进程执行代码。新建forkTest.c文件,
touch forkTest.c 内容如下

#include <unistd.h>
#include <stdio.h>int main(void){printf("main current process pid == %d \n", getpid());// 创建进程int pid = fork();// 这里会分开2个线程 pid等于0 表示三fork新进程执行 不等于0 原来的进程执行代码if (pid == 0) {printf("fork newProgress child process pid = %d parent pid = %d \n", getpid(), getppid());} else {printf("this process pid = %d forkPid = %d parent pid = %d \n", getpid(),pid, getppid());}return 0;
}

然后执行 gcc forkTest.c -o forkTest 编译为二进制文件 ,在执行 ./forkTest 命令执行 查看打印

main current process pid == 7766 
this process pid = 7766 forkPid = 7767 parent pid = 7144 
fork newProgress child process pid = 7767 parent pid = 7766 

http://www.ppmy.cn/news/1474564.html

相关文章

Python图形用户界面的文本文件加密工具

Python图形用户界面的文本文件加密工具 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x5oEvdLE-1720676762602)(https://i-blog.csdnimg.cn/direct/ddf0cd975b2d48a9a2f3b35c55e00add.png)] 设计方案 DES.py DESAlgorithm 类负责管理密钥并提供…

FlutterWeb渲染模式及提速

背景 在使用Flutter Web开发的网站过程中&#xff0c;常常会遇到不同浏览器之间的兼容性问题。例如&#xff0c;在Google浏览器中动画和交互都非常流畅&#xff0c;但在360浏览器中却会出现卡顿现象&#xff1b;在Google浏览器中动态设置图标颜色正常显示&#xff0c;而在Safa…

Linux下解压.tar.gz文件

.tar.gz 是一种常用的压缩包格式&#xff0c;尤其在Unix、Linux以及macOS系统中非常普遍。这个格式结合了两种不同的功能&#xff1a; Tar (.tar): “Tar” 是“Tape Archive”的缩写&#xff0c;最初是为了将数据备份到磁带上而设计的。Tar命令可以将多个文件和目录打包成一个…

markdown文件转pdf

将Markdown文件转换为PDF可以通过多种方法实现&#xff0c;包括使用Visual Studio Code、在线工具、以及命令行工具。 使用Visual Studio Code&#xff1a; 1.安装“Markdown PDF”插件&#xff1a;在VS Code中&#xff0c;通过点击左侧菜单栏的扩展图标&#xff0c;搜…

系统迁移从CentOS7.9到Rocky8.9

我有两台阿里云上的服务器是CentOS7.9&#xff0c;由于CentOS7已经停止支持&#xff0c;后续使用的话会有安全漏洞&#xff0c;所以需要尽快迁移&#xff0c;个人使用的话目前兼容性好的还是RockyLinux8&#xff0c;很多脚本改改就能用了。 一、盘点系统和迁移应用 查看当前系…

聚观早报 | 蚁天鉴2.0发布;理想汽车推送无图NOA

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 7月8日消息 蚁天鉴2.0发布 理想汽车推送无图NOA 特斯拉推送FSD v12.4.3 iQOO Neo9s Pro配色公布 百川智能AI健康…

centos9中mysql指令提示解决方案

CentOS 9 中没有 MySQL 的官方插件&#xff0c;因为 MySQL 不是 CentOS 的默认数据库&#xff0c;它是 MariaDB 的一部分。 如果想要一个命令行提示的 MySQL 客户端&#xff0c;可以使用第三方工具 &#xff0c;如mycli 首先&#xff0c;确保已经安装了 MySQL&#xff0c;且操…

7月10日学习打卡,环形链表+栈OJ

前言 大家好呀&#xff0c;本博客目的在于记录暑假学习打卡&#xff0c;后续会整理成一个专栏&#xff0c;主要打算在暑假学习完数据结构&#xff0c;因此会发一些相关的数据结构实现的博客和一些刷的题&#xff0c;个人学习使用&#xff0c;也希望大家多多支持&#xff0c;有…

解决鸿蒙开发中克隆项目无法签名问题

文章目录 问题描述问题分析解决方案 问题描述 在一个风和日丽的早晨&#xff0c;这是我学习鸿蒙开发的第四天&#xff0c;把文档过了一遍的我准备看看别人的项目学习一下&#xff0c;于是就用git去clone了一个大佬的开源项目&#xff0c;在签名的时候遇到了问题&#xff1a; h…

Visual Studio编译优化选项

目录 /O1 和 /O2 /Ox 内联函数 虚函数优化 代码重排 循环优化 链接时间优化 代码分割 数学优化 其他优化选项 在Visual Studio中&#xff0c;编译优化选项是用于提高程序性能的重要工具。编译器提供了多种优化级别和选项&#xff0c;可以根据不同的需要进行选择。 在…

(Windows环境)FFMPEG编译,包含编译x264以及x265

本文使用 MSYS2 来编译 ffmpeg 一、安装MSYS2 MSYS2 是 Windows 下的一组编译套件&#xff0c;它可以在 Windows 系统中模拟 Linux 下的编译环境&#xff0c;如使用 shell 运行命令、使用 pacman 安装软件包、使用 gcc (MinGW) 编译代码等。 MSYS2 的安装也非常省心&#x…

如何在vue的项目中导入阿里巴巴图标库

阿里巴巴矢量图标库官网&#xff1a;iconfont-阿里巴巴矢量图标库 选择你喜欢的图标&#xff0c;添加入库 点击添加至项目&#xff0c;并新建文件夹&#xff0c;点击确定 选择font-class&#xff0c;点击生成代码 代码生成后&#xff0c;在网站上打开 全选复制到style 点击复制…

大连外贸建站公司wordpress主题模板

Robonaut萝卜纳特WP外贸站模板 适合用于工业机器人公司出口做外贸搭建公司官方网站使用的WordPress模板。 https://www.jianzhanpress.com/?p7091 优衣裳WordPress外贸建站模板 简洁的wordpress外贸独立站模板&#xff0c;适合服装、衣服、制衣外贸公司搭建公司官方网站使用…

【Python】Requests 库使用示例

本文使用Pythonrequests库对微博页面进行数据抓取和简单的数据清洗 使用Requests库进行网络爬虫 requests是一个Python的HTTP客户端库&#xff0c;用于发送HTTP请求。它简单易用&#xff0c;同时提供了足够的功能来处理各种网络请求。 1. 安装requests库 在Python中安装req…

【集成平台】大数据集成平台建设方案(原件word)

基础支撑平台主要承担系统总体架构与各个应用子系统的交互&#xff0c;第三方系统与总体架构的交互。需要满足内部业务在该平台的基础上&#xff0c;实现平台对于子系统的可扩展性。基于以上分析对基础支撑平台&#xff0c;提出了以下要求&#xff1a; 基于平台的基础架构&…

从 Github 安装 R packages 不完全指南

前言 R语言&#xff0c;一个被广泛使用的统计计算和数据分析工具。在日常使用过程中&#xff0c;需要安装实现各种功能、来自各种渠道的工具包&#xff08;packages&#xff09;比如&#xff1a;CRNA或者Github。很多包已经发布在 CRAN 上&#xff0c;使用 install.packages(&q…

【JavaScript脚本宇宙】提升用户体验:探索 JavaScript 库中的浏览器特性支持检测

深入探讨JavaScript库&#xff1a;功能、配置与应用场景 前言 在现代的Web开发中&#xff0c;JavaScript库扮演着至关重要的角色&#xff0c;帮助开发人员简化代码、提高效率、实现更好的用户体验。本文将探讨几个常用的JavaScript库&#xff0c;包括模块加载库、数据绑定库和…

JMeter之脚本录制

前言&#xff1a; 对于一些JMeter初学者来说&#xff0c;录制脚本可能是最容易掌握的技能之一。虽然我不建议录制性能脚本&#xff08;因为录制的脚本比较混乱&#xff0c;必须要通过二次处理才可正常使用&#xff09;&#xff0c;但有时做总比不做要好&#xff0c;是吧…

安卓微信8.0之后如何利用缓存找回的三天之前不可见的朋友圈图片

安卓微信8.0之后如何利用缓存找回的三天之前不可见的朋友圈图片 复习了下安卓程序的知识&#xff0c;我们会了解到&#xff0c;安卓程序清楚数据的时候有两个选项 一个是清除全部数据一个是清除缓存。 清除全部数据表示清除应用数据缓存。 对于安卓微信8.0之后而言&#xff0…

判断对象能否回收的两种方法,以及JVM引用

判断对象能否回收的两种方法&#xff1a;引用计数算法&#xff0c;可达性分析算法 引用计数算法&#xff1a;给对象添加一个引用计数器&#xff0c;当该对象被其它对象引用时计数加一&#xff0c;引用失效时计数减一&#xff0c;计数为0时&#xff0c;可以回收。 特点&#xf…