当前位置: 首页 > article >正文

Android Framework

Android 分区

  • /boot:存放引导程序,包括内核和内存操作程序。
  • /system:相当于电脑 C 盘,存放 Android 系统及系统应用。
  • /recovery:恢复分区,可以进入该分区进行系统恢复。
  • /data:用户数据区,包含了用户的数据:联系人、短信、设置、用户安装的程序。
  • /cache:安卓系统缓存区,保存系统最常访问的数据和应用程序。
  • /misc:包含一些杂项内容,如系统设置和系统功能启用禁用设置。
  • /sdcard:用户自己的存储区,可以存放照片,音乐,视频等文件。

开机加载过程

与计算机启动过程类似,当按下电源按键后,引导芯片代码开始从预定义的地方(固化在 ROM 中的预设代码)开始执行,芯片上的 ROM 会寻找 Bootloader 代码,并加载到内存(RAM)中。

接着 Bootloader 开始执行,Bootloader 会读取 ROM 找到操作系统并将 Linux 内核加载到 RAM 中。

当 Linux 内核启动后会初始化各种软硬件环境,加载驱动程序,挂载根文件系统,Linux 内核加载的最后阶段会启动并执行第一个用户空间进程 init 进程。

Linux 内核启动过程主要涉及 3 个特殊的进程,swapper 进程(又称为 idle 进程,PID = 0), init 进程(PID = 1)和 kthreadd 进程(PID = 2),这三个进程是内核的基础。

  • idle 进程是 Linux 系统第一个进程,是 init 进程和 kthreadd 进程的父进程,这个进程在完成初始化工作后,就开启了init和kthreadd进程,然后自己进入无限循环,负责进程的调度。

  • init 进程是 Linux 系统第一个用户进程,是 Android 系统应用程序的始祖。init进程分为前后两部分,前一部分是在内核启动的,主要是完成创建和内核初始化工作,内容都是跟Linux内核相关的;后一部分是在用户空间启动的,主要完成Android系统的初始化工作。

  • kthreadd 这个进程由idle通过kernel_thread 创建,负责所有内核线程的调度和管理。
    在这里插入图片描述

idle

idle负责完成底层硬件和系统资源的初始化,他其实就是内核初始化用的start_kernel()这个函数所在进程,在start_kernel()执行的末尾,他会把本进程进入循环,处理进程调度,这个时候idle就显示出现了。

在start_kernel()所在进程执行完成之前,也就是idle显示出现之前,会调用函数rest_init(),来启动init进程和kthreadd进程

rest_init

rest_init最重要的就是通过kernel_thread创建了init和kthreadd进程

static noinline void __init_refok rest_init(void)
{int pid;/** 1.C语言中const相当于Java中的final static, 表示常量* 2.struct是结构体,相当于Java中定义了一个实体类,里面只有一些成员变量,{.sched_priority =1 }相当于new,* 然后将成员变量sched_priority的值赋为1*/const struct sched_param param = { .sched_priority = 1 }; //初始化优先级为1的进程调度策略,//取值1~99,1为最小rcu_scheduler_starting(); //启动RCU机制,这个与后面的rcu_read_lock和rcu_read_unlock是配套的,用于多核同步/** We need to spawn init first so that it obtains pid 1, however* the init task will end up wanting to create kthreads, which, if* we schedule it before we create kthreadd, will OOPS.*//** 1.C语言中支持方法传参,kernel_thread是函数,kernel_init也是函数,但是kernel_init却作为参数传递了过去,* 其实传递过去的是一个函数指针,参考[函数指针](http://www.cnblogs.com/haore147/p/3647262.html)* 2.CLONE_FS这种大写的一般就是常量了,跟Java差不多*/kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //用kernel_thread方式创建init进程,//CLONE_FS 子进程与父进程共享相同的文件系统,包括root、当前目录、umask,//CLONE_SIGHAND  子进程与父进程共享相同的信号处理(signal handler)表numa_default_policy(); // 设定NUMA系统的默认内存访问策略pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//用kernel_thread方式创建kthreadd进程,//CLONE_FILES  子进程与父进程共享相同的文件描述符(file descriptor)表rcu_read_lock(); //打开RCU读取锁,在此期间无法进行进程切换/** C语言中&的作用是获得变量的内存地址,参考[C指针](http://www.runoob.com/cprogramming/c-pointers.html)*/kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);// 获取kthreadd的进程描述符,//期间需要检索进程pid的使用链表,所以要加锁rcu_read_unlock(); //关闭RCU读取锁sched_setscheduler_nocheck(kthreadd_task, SCHED_FIFO, &param); //设置kthreadd的进程调度策略,//SCHED_FIFO 实时调度策略,即马上调用,先到先服务,param的优先级之前定义为1complete(&kthreadd_done); // complete和wait_for_completion是配套的同步机制,跟java的notify和wait差不多,//之前kernel_init函数调用了wait_for_completion(&kthreadd_done),//这里调用complete就是通知kernel_init进程kthreadd进程已创建完成,可以继续执行/** The boot idle thread must execute schedule()* at least once to get things moving:*/init_idle_bootup_task(current);//current表示当前进程,当前0号进程init_task设置为idle进程schedule_preempt_disabled(); //0号进程主动请求调度,让出cpu,1号进程kernel_init将会运行,并且禁止抢占/* Call into cpu_idle with preempt disabled */cpu_startup_entry(CPUHP_ONLINE);// 这个函数会调用cpu_idle_loop()使得idle进程进入自己的事件处理循环
}

init

init进程分为前后两部分,前一部分是在内核启动的,主要是完成创建和内核初始化工作,内容都是跟Linux内核相关的;后一部分是在用户空间启动的,主要完成Android系统的初始化工作。

kernel_init

之前在rest_init函数中启动了init进程,在创建完init进程后,会调用kernel_init函数

/** __ref 这个跟之前讲的__init作用一样*/
static int __ref kernel_init(void *unused)
{kernel_init_freeable(); //进行init进程的一些初始化操作/* need to finish all async __init code before freeing the memory */async_synchronize_full();// 等待所有异步调用执行完成,,在释放内存前,必须完成所有的异步 __init 代码free_initmem();// 释放所有init.* 段中的内存mark_rodata_ro(); //arm64空实现system_state = SYSTEM_RUNNING;// 设置系统状态为运行状态numa_default_policy(); // 设定NUMA系统的默认内存访问策略flush_delayed_fput(); // 释放所有延时的struct file结构体if (ramdisk_execute_command) { //ramdisk_execute_command的值为"/init"if (!run_init_process(ramdisk_execute_command)) //运行根目录下的init程序return 0;pr_err("Failed to execute %s\n", ramdisk_execute_command);}/** We try each of these until one succeeds.** The Bourne shell can be used instead of init if we are* trying to recover a really broken machine.*/if (execute_command) { //execute_command的值如果有定义就去根目录下找对应的应用程序,然后启动if (!run_init_process(execute_command))return 0;pr_err("Failed to execute %s.  Attempting defaults...\n",execute_command);}if (!run_init_process("/sbin/init") || //如果ramdisk_execute_command和execute_command定义的应用程序都没有找到,//就到根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动!run_init_process("/etc/init") ||!run_init_process("/bin/init") ||!run_init_process("/bin/sh"))return 0;panic("No init found.  Try passing init= option to kernel. ""See Linux Documentation/init.txt for guidance.");
}

其中比较重要的是,在kernel_init_freeable()中,有对驱动的初始化driver_init

那么kernel_init做了以下事情:

  • 初始化设备和驱动程序
  • 打开标准输入和输出
  • 初始化文件系统

完成后会调用run_init_process进入用户空间的init

用户空间init

在用户空间的init中,做了以下事情:

  • 创建进程会话密钥并初始化属性系统。
  • 进行 SELinux 第二阶段并恢复一些文件安全上下文。
  • 新建 epoll 并初始化子进程终止信号处理函数。
  • 设置其他系统属性并开启系统属性服务。
  • 解析init.rc文件

init.rc文件中会启动servicemanager和zygote

至此,系统的启动过程变为
在这里插入图片描述

zygote

zygote 进程就是 daemon 其中之一,zygote 进程主要负责创建 Java 虚拟机,加载系统资源,启动 SystemServer 进程,以及在后续运行过程中启动普通的应用程序。
在这里插入图片描述
在init.rc中,有这么一句 import /init.${ro.zygote}.rc ,这就是启动zygote.rc来创建zygote进程了;不同厂家有不同的zygote.rc,并且分为32位和64位。
我们只需要知道,不管是哪个版本,zygote.rc的源文件都是app_main.cpp,并且由于zygote在初始化拉起systemserver之后,会负责普通app的启动,所以在app_main.cpp的结尾是

 if (zygote) { // 如果是 zygote 启动模式,则加载 ZygoteInitruntime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) {// 如果是 application 启动模式,则加载 RuntimeInitruntime.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 函数,这个就是要启动虚拟机了,接下来我们分析 start 函数。

创建虚拟机

//frameworks/base/core/jni/AndroidRuntime.cppvoid AndroidRuntime::start(const char* className, const Vector<String8>& options,bool zygote) {// ...// 打印一些日志,获取 ANDROID_ROOT 环境变量// .../* start the virtual machine */JniInvocation jni_invocation;// 初始化JNI,加载 libart.sojni_invocation.Init(NULL);JNIEnv* env;// 创建虚拟机if (startVm(&mJavaVM, &env, zygote) != 0) {return;}// 表示虚拟创建完成,但是里面是空实现onVmCreated(env);/** Register android functions.* 注册 JNI 函数*/if (startReg(env) < 0) {ALOGE("Unable to register all android natives\n");return;}// 接下来的这些语法大家应该比较熟悉了,都是 JNI 里的语法,// 主要作用就是调用 ZygoteInit 类的 main 函数 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.*/// 将 "com.android.internal.os.ZygoteInit" // 转换为 "com/android/internal/os/ZygoteInit"char* slashClassName = toSlashClassName(className);// 找到 classjclass 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 {// 调用 ZygoteInit.main() 方法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");
}

CallStaticVoidMethod 反射调用 ZygoteInit 的 main 函数。

zygote初始化完成后,拉起systemserver,就会进入循环,准备启动普通app

相关文章:

Android Framework

Android 分区 /boot&#xff1a;存放引导程序&#xff0c;包括内核和内存操作程序。/system&#xff1a;相当于电脑 C 盘&#xff0c;存放 Android 系统及系统应用。/recovery&#xff1a;恢复分区&#xff0c;可以进入该分区进行系统恢复。/data&#xff1a;用户数据区&#…...

JVM对象分配与程序崩溃排查

一、new 对象在 JVM 中的过程 在 JVM 中通过 new 关键字创建对象时&#xff0c;会经历以下步骤&#xff1a; 内存分配 对象的内存分配在 堆&#xff08;Heap&#xff09; 中&#xff0c;优先在 新生代&#xff08;Young Generation&#xff09; 的 Eden 区 分配。分配方式取决…...

OpenMCU(六):STM32F103开发板功能介绍

概述 距上一篇关于STM32F103的FreeRTOS博客的发布已经过去很长时间没有更新了。在这段时间内&#xff0c;大家可以看到博主发表了一系列的关于使用qemu 模拟实现STM32F103的博客&#xff0c;博主本来想借助qemu开发stm32F103相关的一些软件功能&#xff0c;博主开发出来并成功运…...

(自用)Java学习-5.12(Redis,B2C电商)

一、Redis 核心知识 缓存作用 提升性能&#xff1a;内存读写速度&#xff08;读 10w/s&#xff0c;写 8w/s&#xff09;远超 MySQL&#xff08;读 3w/s&#xff0c;写 2w/s&#xff09;减少数据库压力&#xff1a;通过内存缓存热点数据&#xff0c;避免频繁 SQL 查询分类&#…...

Rspack:字节跳动自研 Web 构建工具-基于 Rust打造高性能前端工具链

字节跳动开源了一款采用 Rust 开发的前端模块打包工具&#xff1a;Rspack&#xff08;读音为 /ɑrspk/&#xff09;。 据介绍&#xff0c;Rspack 是一个基于 Rust 的高性能构建引擎&#xff0c;具备与 Webpack 生态系统的互操作性&#xff0c;可以被 Webpack 项目低成本集成&a…...

深度解析LLM参数:Top-K、Top-p和温度如何影响输出随机性?

许多大模型具有推理参数&#xff0c;用于控制输出的“随机性”。常见的几个是 Top-K、Top-p&#xff0c;以及温度。 Top-p&#xff1a; 含义&#xff1a;Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger t…...

高速系统设计实例设计分析

在上几章的内容中&#xff0c;我们从纯粹高速信号的理论分析&#xff0c;到 Cadence 工具的具体使用都做了详细的讲解和介绍。相信读者通过前面章节的学习&#xff0c;已经对高速系统的设计理念及 Cadence 相应的设计流程和工具有了一个基本的认识。但是&#xff0c;对于高速电…...

查看购物车

一.查看购物车 查看购物车使用get请求。我们要查看当前用户的购物车&#xff0c;就要获取当前用户的userId字段进行条件查询。因为在用户登录时就已经将userId封装在token中了&#xff0c;因此我们只需要解析token获取userId即可&#xff0c;不需要前端再传入参数了。 Control…...

疑难杂症:dex安装部署

方式一、源码包下载 wget https://github.com/dexidp/dex/archive/refs/tags/v2.42.1.tar.gz 方式二、git方式拉取源码编译&#xff1a; Getting Started | $ git clone https://github.com/dexidp/dex.git 编译 $ cd dex/ $ make build 启动 ./bin/dex serve examples/…...

【idea】快捷键ctrl+shift+F(Find in files)不起作用

问题描述 在idea中使用快捷键CtrlShiftF&#xff0c;进行内容的搜索&#xff0c;但是弹不出对话框、或有时候能弹出有时候又弹不出。 原因分析 1.怀疑是缓存问题&#xff1f;--清空缓存重启也没什么作用 2.怀疑是idea的问题&#xff1f;--有时行、有时不行&#xff0c;而且…...

开发工具分享: Web前端编码常用的在线编译器

1.OneCompiler 工具网址&#xff1a;https://onecompiler.com/ OneCompiler支持60多种编程语言&#xff0c;在全球有超过1280万用户&#xff0c;让开发者可以轻易实现代码的编写、运行和共享。 OneCompiler的线上调试功能完全免费&#xff0c;对编程语言的覆盖也很全&#x…...

EnumUtils:你的枚举“变形金刚“——让枚举操作不再手工作业

各位枚举操控师们好&#xff01;今天要介绍的是Apache Commons Lang3中的EnumUtils工具类。这个工具就像枚举界的"瑞士军刀"&#xff0c;能让你的枚举操作从石器时代直接跃迁到星际文明&#xff01; 一、为什么需要EnumUtils&#xff1f; 手动操作枚举就像&#xf…...

智启未来:新一代云MSP管理服务助力企业实现云成本管理和持续优化

在数字化转型浪潮下&#xff0c;企业纷纷寻求更高效、更经济的运营方式。随着云计算技术的深入应用&#xff0c;云成本优化已成为企业普遍关注的核心议题。 过去&#xff0c;传统云运维服务往往依赖于人力外包&#xff0c;缺乏系统性、规范性的管理&#xff0c;难以有效降低云…...

window 显示驱动开发-将虚拟地址映射到内存段(二)

在将虚拟地址映射到段的一部分之前&#xff0c;视频内存管理器调用显示微型端口驱动程序的 DxgkDdiAcquireSwizzlingRange 函数&#xff0c;以便驱动程序可以设置用于访问可能重排的分配位的光圈。 驱动程序既不能将偏移量更改为访问分配的 PCI 光圈&#xff0c;也不能更改分配…...

C++:构造函数

构造函数是类的六个默认成员函数之一&#xff0c;这里的默认是指我们不写&#xff0c;编译器会自己生成的。 构造函数其目的是初始化对象&#xff0c;不是开空间。 其特征如下&#xff1a; 1.函数名与类名相同 2.没有返回值&#xff0c;意思是不用在函数前面写void。 3.对…...

【文心智能体】使用文心一言来给智能体设计一段稳定调用工作流的提示词

&#x1f339;欢迎来到《小5讲堂》&#x1f339; &#x1f339;这是《文心智能体》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。&#x1f339; &#x1f339;温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff0…...

K8S中构建双架构镜像-从零到成功

背景介绍 公司一个客户的项目使用的全信创的环境&#xff0c;服务器采用arm64的机器&#xff0c;而我们的应用全部是amd64的&#xff0c;于是需要对现在公司流水线进行arm64版本的同步镜像生成。本文介绍从最开始到最终生成双架构的全部过程&#xff0c;以及其中使用的相关配置…...

pth的模型格式怎么变成SafeTensors了?

文章目录 背景传统模型格式的安全隐患效率与资源瓶颈跨框架兼容性限制Hugging Face 的解决方案&#xff1a;SafeTensors行业与社区的推动SafeTensors 的意义总结 背景 最近要找一些适合embedding的模型&#xff0c;在huggingface模型库上看到一些排名比较靠前的&#xff0c;准…...

iOS safari和android chrome开启网页调试与检查器的方法

手机开启远程调试教程&#xff08;适用于 Chrome / Safari&#xff09; 前端移动端调试指南&#xff5c;适用 iPhone 和 Android&#xff5c;WebDebugX 出品 本教程将详细介绍如何在 iPhone 和 Android 手机上开启网页检查器&#xff0c;配合 WebDebugX 实现远程调试。教程包含…...

c语言第一个小游戏:贪吃蛇小游戏03

我们为贪吃蛇的节点设置为一个结构体&#xff0c;构成贪吃蛇的身子的话我们使用链表&#xff0c;链表的每一个节点是一个结构体 显示贪吃蛇身子的一个节点 我们这边node就表示一个蛇的身体 就是一小节 输出结果如下 显示贪吃蛇完整身子 效果如下 代码实现 这个hasSnakeNode(…...

​​​​​​​大规模预训练范式(Large-scale Pre-training)

大规模预训练指在巨量无标注数据上&#xff0c;通过自监督学习训练大参数量的基础模型&#xff0c;使其具备通用的表征与推理能力。其重要作用如下&#xff1a; 一 跨任务泛化 单一模型可在微调后处理多种NLP&#xff08;自然语言处理&#xff09;、CV&#xff08;计算机视觉…...

基于Flink的用户画像 OLAP 实时数仓统计分析

1.基于Flink的用户画像 OLAP 实时数仓统计分析 数据源是来自业务系统的T日数据&#xff0c;利用kakfa进行同步 拼接多个事实表形成大宽表&#xff0c;优化多流Join方式&#xff0c;抽取主键和外键形成主外键前置层&#xff0c;抽取外键和其余内容形成融合层&#xff0c;将4次事…...

React Native踩坑实录:解决NativeBase Radio组件在Android上的兼容性问题

React Native踩坑实录&#xff1a;解决NativeBase Radio组件在Android上的兼容性问题 问题背景 在最近的React Native项目开发中&#xff0c;我们的应用在iOS设备上运行良好&#xff0c;但当部署到Android设备时&#xff0c;进入语言设置和隐私设置页面后应用崩溃。我们遇到了…...

iptables实现DDos

最近有客户要定制路由器的默认防火墙等级&#xff0c;然后涉及到了DDos规则&#xff0c;对比客户提供的规则发现我们现有的规则存在明显的错误&#xff0c;在此记录一下如何使用iptables防护DDoS攻击 直接贴一下规则 #开启TCP SYN Cookies 机制 sysctl -w net.ipv4.tcp_synco…...

WPF之高级绑定技术

文章目录 引言多重绑定&#xff08;MultiBinding&#xff09;基本概念实现自定义IMultiValueConverterMultiBinding在XAML中的应用示例使用StringFormat简化MultiBinding 优先级绑定&#xff08;PriorityBinding&#xff09;基本概念PriorityBinding示例实现PriorityBinding的后…...

调出事件查看器界面的4种方法

方法1. 方法2. 方法3. 方法4....

使用vite重构vue-cli的vue3项目

一、修改依赖 首先修改 package.json&#xff0c;修改启动方式与相应依赖 移除vue-cli并下载vite相关依赖&#xff0c;注意一些peerDependency如fast-glob需要手动下载 # 移除 vue-cli 相关依赖 npm remove vue/cli-plugin-babel vue/cli-plugin-eslint vue/cli-plugin-rout…...

Go-GJSON 组件,解锁 JSON 读取新姿势

现在的通义灵码不但全面支持 Qwen3&#xff0c;还支持配置自己的 MCP 工具&#xff0c;还没体验过的小伙伴&#xff0c;马上配置起来啦~ https://click.aliyun.com/m/1000403618/ 在 Go 语言开发领域&#xff0c;json 数据处理是极为常见的任务。Go 标准库提供了 encoding/jso…...

Java详解LeetCode 热题 100(14):LeetCode 56. 合并区间(Merge Intervals)详解

文章目录 1. 题目描述2. 理解题目3. 解法一:排序 + 一次遍历法3.1 思路3.2 Java代码实现3.3 代码详解3.4 复杂度分析3.5 适用场景4. 解法二:双指针法4.1 思路4.2 Java代码实现4.3 代码详解4.4 复杂度分析4.5 与解法一的比较5. 解法三:TreeMap法5.1 思路5.2 Java代码实现5.3 …...

将Docker镜像变为可执行文件?体验docker2exe带来的便捷!

在现代软件开发中,容器化技术极大地改变了应用程序部署和管理的方式。Docker,作为领先的容器化平台,已经成为开发者不可或缺的工具。然而,对于不熟悉Docker的用户来说,接触和运行Docker镜像可能会是一个复杂的过程。为了解决这一问题,docker2exe项目应运而生。它提供了一…...