Android系统启动流程分析

news2024/10/7 16:15:50

       当按下Android系统的开机电源按键时候,硬件会触发引导芯片,执行预定义的代码,然后加载引导程序(BootLoader)到RAM,Bootloader是Android系统起来前第一个程序,主要用来拉起Android系统程序,Android系统被拉起首先肯定会启动Linux内核。

备注: 我们再刷机时候,经常看到工具软件会让我们切换bootloader/loader模式,其实就是重新激活bootloader程序,然后再拷贝新的镜像文件重新刷机,就是通过这个程序重新初始化硬件设备,建立内存空间映射。

        我们也知道一个系统肯定会存在一些重要服务和进程来支持整个系统正常运作。  那么Android系统中肯定也存在这种重要进程,如下:  

序号进程名称概述
1init进程Linux系统中用户空间第一个进程
2zygote进程所有App进程的父进程,Zygote Init 
3system_server进程各大系统服务的载体,forkSystemServer / SystemServer
4servicemanager进程binder服务的大管家,守护进程循环运行在binder_loop 

内核启动首先会第一个创建init进程,进程号是1,是所有用户空间的鼻祖,init进程又会启动servicemanager(binder服务管家) 和zygote进程(Java进程鼻祖),zygote进程会创建system_server进程以及各种app进程,大致启动关系如下: 

源码分析:基于 android10 

inity源码分析

/system/core/init/main.cpp  

 int main(int argc, char** argv) {
  #if __has_feature(address_sanitizer)
      __asan_set_error_report_callback(AsanReportCallback);
  #endif
  
      if (!strcmp(basename(argv[0]), "ueventd")) {
          return ueventd_main(argc, argv);
      }
  
      if (argc > 1) {
          if (!strcmp(argv[1], "subcontext")) {
              android::base::InitLogging(argv, &android::base::KernelLogger);
              const BuiltinFunctionMap function_map;
  
              return SubcontextMain(argc, argv, &function_map);
          }
  
          if (!strcmp(argv[1], "selinux_setup")) {
              return SetupSelinux(argv);
          }
  
          if (!strcmp(argv[1], "second_stage")) {
              return SecondStageMain(argc, argv);
          }
      }
  
      return FirstStageMain(argc, argv);
  }

main函数有四个参数入口: 

1.参数有ueventd进入 uevent_main  

2. 参数中有subcontext,进入InitLogging和 SubcontextMain 

3. 参数中有selinux_setup,进入SetupSelinux 

4. 参数中有second_state,进入SecondStageMain 

执行顺序如下: 首先会进入FirstStateMain ,主要执行是初始化环境变量,挂载和创建基本的文件系统,并设置访问权限,挂载system、cache、data等系统分区 。 之后进入  SetupSelinxu 根据源码我们可以看到FirstStateMain最后传送了参数selinux_setup 

int FirstStageMain(int argc, char** argv) {
     
      .......  

      const char* path = "/system/bin/init";
      const char* args[] = {path, "selinux_setup", nullptr};
      execv(path, const_cast<char**>(args));
  
      // execv() only returns if an error happened, in which case we
      // panic and never fall through this conditional.
      PLOG(FATAL) << "execv(\"" << path << "\") failed";
  
      return 1;
 }

SetupSelinux主要工作谁设置SELinux安全策略,之后进入SecondStageMain 。  

int SecondStageMain(int argc, char** argv) {
        ..... 

      // oom_scroe_adj 为-1000时候相当与关闭OOM机制。 范围 -1000 - 1000 
      if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {
          LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error();
      }
  
      // 启用全局Seccomp 
      GlobalSeccomp();
 
      // 设置所有进程都可以访问的会话密钥环
       keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
  
      // 标记booting中 
      close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
  
         //初始化属性服务,指定文件读取属性
      property_init();

      ....... 
 
      // 为第二阶段设置 SELinux。
      SelinuxSetupKernelLogging();
      SelabelInitialize();
      SelinuxRestoreContext();
  
      // android封装好的Epoll开始初始化 
      Epoll epoll;
      if (auto result = epoll.Open(); !result) {
          PLOG(FATAL) << result.error();
      }
  
     // 注册singelfd信号,为创建handler处理子进程终止信号 
      InstallSignalFdHandler(&epoll);
  

      .....  

     // 注册property_set_fd, 设置其他系统属性并开启系统属性服务   
      StartPropertyService(&epoll);
      MountHandler mount_handler(&epoll);
   
     ....... 
  
  
      ActionManager& am = ActionManager::GetInstance();
      ServiceList& sm = ServiceList::GetInstance();
     //解析init.rc 等文件,建立rc文件的action,service,启动其他进程。 
      LoadBootScripts(am, sm);
  
      
      ..... 

      am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
  
      //rc文件中触发器为 on earyly-init 语句 
      am.QueueEventTrigger("early-init");
  
      // 等待冷插拔设备初始化完成 
    am.QueueBuiltinAction(wait_for_coldboot_done_action,"wait_for_coldboot_done");
      // so that we can start queuing up actions that require stuff from /dev.
      am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
      am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
      am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");

         // 设备组合键初始化操作 
      Keychords keychords;
      am.QueueBuiltinAction(
          [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
              for (const auto& svc : ServiceList::GetInstance()) {
                  keychords.Register(svc->keycodes());
              }
              keychords.Start(&epoll, HandleKeychord);
              return Success();
          },
          "KeychordInit");
      am.QueueBuiltinAction(console_init_action, "console_init");
  
      // 开始触发rc文件中为  on init 的语句  
      am.QueueEventTrigger("init");
  
      // Starting the BoringSSL self test, for NIAP certification compliance.
      am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
  
      // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
      // wasn't ready immediately after wait_for_coldboot_done
      am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
  
      // Initialize binder before bringing up other system services
      am.QueueBuiltinAction(InitBinder, "InitBinder");
  
      //不要在充电器模式下挂载文件系统或启动核心系统服务。.
      std::string bootmode = GetProperty("ro.bootmode", "");
      if (bootmode == "charger") {
          am.QueueEventTrigger("charger");
      } else {
          am.QueueEventTrigger("late-init");
      }   
     // 根据属性的当前状态运行所有属性触发器。.
      am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
  
      while (true) {
          //进入死循环s.
          auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
  
          if (do_shutdown && !shutting_down) {
              do_shutdown = false;
              if (HandlePowerctlMessage(shutdown_command)) {
                  shutting_down = true;
              }
          }
  
          if (!(waiting_for_prop || Service::is_exec_service_running())) {
              am.ExecuteOneCommand();
          }
          if (!(waiting_for_prop || Service::is_exec_service_running())) {
              if (!shutting_down) {
                  auto next_process_action_time = HandleProcessActions();
 
                 // 如果有一个进程需要重新启动,请及时唤醒。
                 if (next_process_action_time) {
                     epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                              *next_process_action_time - boot_clock::now());
                      if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
                  }
             }
  
              // 如果还有更多工作要做,请立即醒来。
              if (am.HasMoreCommands()) epoll_timeout = 0ms;
         }
  
          // 这里一直等待循环事件 过来
          if (auto result = epoll.Wait(epoll_timeout); !result) {
             LOG(ERROR) << result.error();
         }
     }
  
     return 0;
 }

其中最关键就是解析init.rc 文件,并且按照rc文件定义去启动服务,然后开启死循环,用于接受epoll事件  

init.rc文件

init.rc文件在 /system/core/rootdir/init.rc 

 import /init.environ.rc
 import /init.usb.rc
 import /init.${ro.hardware}.rc
 import /vendor/etc/init/hw/init.${ro.hardware}.rc
 import /init.usb.configfs.rc
 import /init.${ro.zygote}.rc

zygote服务启动

Zygote是由init进程通过解析init.zygote.rc文件而创建

我们可以看到/system/core/rootdir/下有4个init.zygote.rc文件,通过ro.zygote配置得值去读取对应配置文件,这里以init.zygote64.rc  为例子 


 service zygote /system/bin/app_process64 -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 write /sys/android_power/request_state wake
     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

对应的可执行程序是app_process64 , 对应的源文件是/frameworks/base/cmds/app_process/app_main.cpp

zygote启动过程如下: 

1.创建了AppRuntime,并且调用了start方法。 

2. AndroidRuntime调用了startVm创建了虚拟机,调用startReg注册JNI函数。 

3.通过JNI调用ZygoteInit.main 进入Java  。

4. 建立socket通道,zygote作为通讯服务端,用于响应客户端请求。 

5. 预加载通用类,drawable,color资源,共享库等,用于提高app启动效率。 

6. forksytem_server进程,上层java framework的运行载体。 

参考文章:Android系统启动-zygote篇 - Gityuan博客 | 袁辉辉的技术博客袁辉辉, Gityuan, Android博客, Android源码, Flutter博客,Flutter源码http://gityuan.com/2016/02/13/android-zygote/

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

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

相关文章

设计模式——装饰者模式

装饰者模式 定义 动态地给一个对象添加一些额外的职责。就增加功能来说&#xff0c;装饰者模式相比生成子类更灵活 优缺点和应用场景 优点 拓展某个类的功能&#xff0c;附加功能动态地给一个对象增加功能&#xff0c;这些功能可以动态地撤销需要给一批兄弟类进行改装或加…

Databend 开源周报第 101 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 教程 | 使用 AW…

【技能实训】DMS数据挖掘项目-Day05

文章目录 任务6【任务6.1】创建数据分析接口【任务6.2】创建数据过滤抽象类【任务6.3】编写日志数据分析类【任务6.4】创建日志数据分析测试类&#xff0c;测试任务6.1-6.3中的程序&#xff0c;演示日志信息的采集、分析及打印输出 任务6 【任务6.1】创建数据分析接口 在com.…

单片机如何开启TFTP服务器

一、开发测试环境 野火stm32开发板&#xff0c;w5500模块&#xff0c;Tftpd64软件&#xff0c;rt-thread操作系统和netutils软件包&#xff08;网络小工具集&#xff09;。&#xff08;RT-Thread 目前支持 TFTP 服务器和 TFTP 客户端&#xff09;。 二、tftp介绍-简单文件传输的…

matlab学习指南(1):matlab初步入门详细介绍

&#x1f305;*&#x1f539;** φ(゜▽゜*)♪ **&#x1f539;*&#x1f305; 欢迎来到馒头侠的博客&#xff0c;该类目主要讲数学建模的知识&#xff0c;大家一起学习&#xff0c;联系最后的横幅&#xff01; 喜欢的朋友可以关注下&#xff0c;私信下次更新不迷路&#xff0…

Linux C程序开发,多线程编程、网络编程

目录 多线程编程 网络编程 Linux C程序开发是指在Linux操作系统下使用C语言进行开发的过程。Linux是一种开源的操作系统&#xff0c;具有稳定性、安全性和灵活性等优点&#xff0c;因此在很多领域都得到了广泛的应用。 多线程编程 多线程编程是指在一个程序中同时运行多个线…

二、OAuth2 client对接Spring Authorization Server

这里用的是授权码模式 搭建&#xff1a;Spring Authorization Server 代码结构如下&#xff1a; 代码实现 添加依赖 <dependency> <groupId>org.springframework.boot</groupId> …

Failed to connect to github.com port 443: Connection refused问题解决

文章目录 一、问题描述&#xff1a;Failed to connect to github.com port 443: Connection refused问题解决二、解决方法一&#xff1a;排查代理问题1、尝试重置代理或者取消代理的方式2、添加全局代理 三、解决方法二&#xff1a;排查DNS解析问题1、第一步&#xff1a;查找gi…

软考高级之系统架构师系列之软件开发模型

概述 如标题所述。本文面向于软考高级&#xff0c;具体来说是系统架构师。 本来几乎是纯粹的理论知识汇总&#xff0c;用于应付软考&#xff0c;在理解基础上注意抠字眼。 软件开发方法 分类描述结构化法强调用户至上&#xff0c;严格区分工作阶段&#xff0c;每阶段都有任…

老电脑如何用U盘重装系统?老电脑用U盘重装系统教程

老电脑如何用U盘重装系统&#xff1f;用户利用U盘来给老电脑重装系统&#xff0c;能够帮助解决老电脑运行缓慢、系统出现故障或感染病毒等问题&#xff0c;通过重装系统&#xff0c;可以清除旧的系统文件和应用程序&#xff0c;重新安装一个干净且高效的操作系统&#xff0c;那…

Java8实战-总结1

Java8实战-总结1 基础知识流处理用行为参数化把代码传递给方法并行与共享的可变数据Java需要演变 Java 中的函数 基础知识 流处理 流是一系列数据项&#xff0c;一次只生成一项。程序可以从输入流中一个一个读取数据项&#xff0c;然后以同样的方式将数据项写入输出流。一个程…

TypeScript 学习笔记(二):接口与类型别名、字面量类型

一、接口的定义 在面向对象的编程中&#xff0c;接口是一种规范的定义&#xff0c;它定义了行为和动作的规范&#xff0c;在程序设计里面&#xff0c;接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范&#xff0c;接口不关心这些类的内部状态数据&#xff0…

qt 聊天室

服务器端 widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);//实例化一个服务器对象server new QTcpServer(this);}Widget::~Widget() {delete ui; }…

ELK插件介绍

ELK插件介绍 一、Grok 正则捕获插件1、概述2、内置正则表达式调用3、自定义表达式调用 二、multiline 多行合并插件1、概念2、安装3、使用 multiline 插件 三、date 时间处理插件1、概念2、操作3、时间戳详解 四、mutate 数据修改插件1、概念2、案例 一、Grok 正则捕获插件 1、…

上海亚商投顾:沪指震荡反弹 新能源车产业链再度爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 沪指今日震荡反弹&#xff0c;创业板指午后涨近1%。新能源车产业链再度爆发&#xff0c;整车、零部件、智能驾驶等…

python接口自动化(二十九)--html测试报告通过邮件发出去——上(详解)

简介 前边几篇&#xff0c;已经教小伙伴们掌握了如何生成HTML的测试报告&#xff0c;那么生成测试报告&#xff0c;我们也不能放在那里不管了&#xff0c;这样即使你报告在漂亮&#xff0c;领导也看不到。因此如果想向领导汇报工作&#xff0c;不仅需要提供更直观的测试报告。而…

大学生活动社交小程序开发笔记(1)

可研分析 大学生活动社交小程序是一种基于移动互联网的社交平台&#xff0c;旨在为大学生提供一个方便、快捷、安全的社交和活动交流平台 功能规划 活动发布&#xff1a;平台可以发布将要举行的活动&#xff0c;包括时间、地点、费用等信息&#xff0c;并邀请其他用户参加。…

《UNUX环境高级编程》(8)进程控制

1、引言 2、进程标识 每个进程都用一个唯一的非负整数标识&#xff0c;即为进程id&#xff1a;pid。进程ID是可以复用的&#xff0c;当一个进程终止时&#xff0c;其进程ID就可以用来标识其他进程。系统中有一些专用进程&#xff1a; 进程ID为0的是调度进程&#xff0c;也称交…

【软件测试】Git 远程仓库的使用(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 查看远程仓库 想…

Docker笔记 容器的数据卷

1. 数据卷概念 思考&#xff1a; Docker容器删除后&#xff0c;在容器中产生的数据还在吗&#xff1f; 答案是不在了&#xff0c;数据存放在容器中&#xff0c;如果将容器删除&#xff0c;数据也会被一并删除 Docker容器和外部机器可以直接交换文件吗&#xff1f; 答案是不…