Android Framework

news2025/5/13 7:51:11

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的优先级之前定义为1

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

创建虚拟机

//frameworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options,
                           bool zygote) {
                           // ...
  // 打印一些日志,获取 ANDROID_ROOT 环境变量
  // ...

  /* start the virtual machine */
  JniInvocation jni_invocation;
  // 初始化JNI,加载 libart.so
  jni_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);
  // 找到 class
  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 {// 调用 ZygoteInit.main() 方法
      env->CallStaticVoidMethod(startClass, startMeth, strArray);

      #if 0
      if (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

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

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

相关文章

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

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

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

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

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

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

查看购物车

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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…

数据治理域——数据治理体系建设

摘要 本文主要介绍了数据治理系统的建设。数据治理对企业至关重要&#xff0c;其动因包括应对数据爆炸增长、提升内部管理效率、支撑复杂业务需求、加强风险防控与合规管理以及实现数字化转型战略。其核心目的是提升数据质量、统一数据标准、优化数据资产管理、支撑业务发展和…

onGAU:简化的生成式 AI UI界面,一个非常简单的 AI 图像生成器 UI 界面,使用 Dear PyGui 和 Diffusers 构建。

​一、软件介绍 文末提供程序和源码下载 onGAU&#xff1a;简化的生成式 AI UI界面开源程序&#xff0c;一个非常简单的 AI 图像生成器 UI 界面&#xff0c;使用 Dear PyGui 和 Diffusers 构建。 二、Installation 安装 文末下载后解压缩 Run install.py with python to setup…

【第52节】Windows编程必学之从零手写C++调试器下篇(仿ollydbg)

目录 一、引言 二、调试器核心功能设计与实现 三、断点功能 四、高级功能 五、附加功能 六、开发环境与实现概要 七、项目展示及完整代码参考 八、总结 一、引言 在软件开发领域&#xff0c;调试器是开发者不可或缺的工具。它不仅能帮助定位代码中的逻辑错误&#xff0…

uni-app学习笔记五--vue3插值表达式的使用

vue3快速上手导航&#xff1a;简介 | Vue.js 模板语法 插值表达式 最基本的数据绑定形式是文本插值&#xff0c;它使用的是“Mustache”语法 (即双大括号)&#xff1a; <span>Message: {{ msg }}</span> 双大括号标签会被替换为相应组件实例中 msg 属性的值。同…

C++类与对象(二):六个默认构造函数(一)

在学C语言时&#xff0c;实现栈和队列时容易忘记初始化和销毁&#xff0c;就会造成内存泄漏。而在C的类中我们忘记写初始化和销毁函数时&#xff0c;编译器会自动生成构造函数和析构函数&#xff0c;对应的初始化和在对象生命周期结束时清理资源。那是什么是默认构造函数呢&…

从逻辑学视角探索数学在数据科学中的系统应用:一个整合框架

声明&#xff1a;一家之言&#xff0c;看个乐子就行。 图表采用了两个维度组织知识结构&#xff1a; 垂直维度&#xff1a;从上到下展示了知识的抽象到具体的演进过程&#xff0c;分为四个主要层级&#xff1a; 逻辑学基础 - 包括数理逻辑框架和证明理论数学基础结构 - 涵盖…