Android 启动流程初探

191 阅读10分钟

Android启动流程分析:

  • 整体示意图:

图片.png

  • 补充说明:

    • 整体上可以分为内核空间与用户空间:

      • 内核空间:Loader,kernel
      • 用户空间:C/C++ FrameWork Native + Java FrameWork + Apps
    • BootLoder:执行后,引导操作系统启动

      • 可以认为是Windows的Bios
    • RAM:

      • 随机存取存储器(Random Access Memory,RAM)又称作“随机存储器”,是与CPU直接交换数据的内部存储器,也叫主存(内存)。它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介
    • DRAM和DRAM:

      • RAM分为静态的(SRAM)和动态的(DRAM),分别对应高速缓存和主存。

        存储单元结构不同导致了RAM容量的不同。一个DRAM存储单元大约需要一个晶体管和一个电容(不包括行读出放大器等),而一个SRAM存储单元大约需要六个晶体管。DRAM和SDRAM由于实现工艺问题,容量较SRAM大,但是读写速度不如SRAM。一个是静态的,一个是动态的,静态的是用的双稳态触发器来保存信息,而动态的是用电子,要不时的刷新来保持。

    • ROM:

      • 只读存储器(Read Only Memory,ROM)。ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地、方便地加以改写。ROM所存数据稳定,断电后所存数据也不会改变;一般是指主板bios或者显卡bios。
    • RAM与ROM区别:

      • 两者的最大区别是RAM在断电以后保存在上面的数据会自动消失,而ROM不会自动消失,可以长时间断电保存。
    • RAM和ROM的硬盘:

      • RAM和ROM都是内存

        硬盘是外存

        手机参数描述的ROM容量其实是ROM和硬盘大小。

    • Kernel:

      1. BootLoader去启动Android的第一个进程,idle进程(pid = 0);

        • idle进程又叫做swapper进程

          • 进行初始化操作:进程管理,内存管理,加载Binder驱动等
          • fork启动kthreadd进程(pid = 2):对应内核空间
          • fork启动init进程(pid = 1):对应用户空间
          • 干完这两件事后,idle基本上就不干事情了,空闲了;
      2. kthreadd:创建内核中的线程,相当于内核鼻祖;

        • 内核中的线程都是由kthreadd搞出来的
        • 内核工作线程:kworker
        • 软中断线程:ksofttriqd
        • 内核守护线程:thermal
    • C/C++ FrameWork Native:

      • init进程(pid = 1),为用户空间的鼻祖;

        • 用户空间的进程都是init直接或者间接fork出来
        • fork创建zygote进程:zygote,Java进程的鼻祖
    • Java FrameWork层:

      • zygote首先fork出SystemServer进程

        • SystemServer:启动90+服务
        • 后面的AMS,WMS,PMS都是属于SystemServer这个进程的;
    • Apps层:

      • 对于App进程创建,是SystemServer进程通知zygote进程,由zygote来fork出APP进程
    • 启动相关:

      • 创建进程:fork

        • 只要不调用这个,那么代码都在一个线程中;
        • 写时拷贝,fork出来的子进程继承父进程的东西
      • 创建线程:new Thread

      • 并且在内核中并无严格的进程,线程划分;由于内存共享,甚至可以认为内核中都是线程,

Init进程启动过程:

  • 整体流程图:

    image-20220502164934970

  • 补充说明:

    • init中到底干了什么?

       init处理的重要事情:
       1.挂载文件
       2.设置selinux ---> 安全策略
       3.开启属性服务,注册到epoll机制中
       4.解析init.rc 
       5.循环处理脚本 ---> 启动zygote
       6.循环等待
      
    • /system/bin/init:

      • 这个程序有什么用?

        • init程序执行后会启动init进程
      • 这个程序是由谁启动的:从kernel层去找:

        1. kernel/common/init/main.c

          • 执行:kernel_init方法

            • 这个方法里面执行:

              • try_to_run_init_process("/bin/init")
        2. try_to_run_init_process("/bin/init")执行逻辑:

          • kernel_execve将init程序文件名放进来,就执行了init程序
    • init 执行细节

      • 源码分析入口:

        • 在android.bp中去找:cc_binarty里面的src属性值:main.cpp
        • init初始化过程,会调用多次
      • init/main.cpp:

        • 第一次调用main不带参数,第二次调用main带上参数
      • SetupSelinux细节

        • linux这块的安全策略 -- Android --- 权限 最小权限原则
      • SecondStageMain细节:

        • 初始化属性域

           PropertyInit(); -- 初始化属性域
          
        • init还会处理子进程终止信号 -- 杀掉僵尸进程

          • 僵尸进程:挂掉了但是仍然存在,占用资源

             InstallSignalFdHandler(&epoll);
             InstallInitNotifier(&epoll);
             StartPropertyService(&property_fd);
            
        • 匹配关系:

           GetBuiltinFunctionMap 匹配命令和函数之间的关系
           mkdir -- 函数匹配
          
    • stdio:重定向输入输出

      • Linux中一切皆文件

      • 将IO中的东西写入管道文件:/dev/null

        • 此时这个文件就拥有了IO能力
    • 挂载类似于插入U盘:

      • 将U盘与实际的文件夹绑定,这样U盘中才能显示文件内容
    • 补充细节:

      • Android中去编译代码,之前使用android.mk现在使用android.bp

zygote:孵化器(创建android中的进程)

  • 整体流程:

图片.png

init.rc 解析相关:

  • Zygote是个什么样的角色?

    • Java层的鼻祖,从zygote启动后,才会出现 .java文件

      • 一部分在C++framework native;
      • 一部分在 Java层
  • init.rc是怎么解析的?

    1. 首先会导入zygote.rc,
    2. 触发zygote-start:涉及到rc语法
     trigger zygote-start
    
    1. zygote启动:四个文件(init.zygote)

      • 对应的zygote文件:

      图片.png

    2. 然后会去解析四个文件

  • 解析的四个文件分类

    1. 32位系统init.zygote32.rc

    2. 64位系统:init.zygote64.rc

    3. 主64次32:init.zygote64_32.rc

      • 首先执行64位系统方式启动,不行才32
    4. 主32次64:init.zygote32_64.rc

  • 文件解析具体流程:32位系统init.zygote32.rc

    • 首先启动指定路径下的zygote服务
    • 执行main方法
    • 设置参数:pid、ppid等
    • 定义重启虚拟机重启逻辑:
  • 文件解析细节:zygote,init等这种关键性的进程被kill后,整个android系统都会重启;

    • 虚拟机切换至android7.0:

    • 打开命令行adb shell

    • 查看所有进程:ps

    • 找到init进程的pid:6158

    • 杀死init进程:kill -9 6158

      • 整个虚拟机都会重启

      • 注意有个权限:su

zygote启动流程:

  • 总结:

     zygote总结:
     native:java需要运行时环境,但是init启动后没有这个
     1.初始化运行环境,创建jvm Android的虚拟机  AndroidRuntime ART 
     2.注册jni,打通native <---> java
     3.调用 zygoteinit.main
     java
     1.预加载 -- 加快进程启动
     2.socket  让别人通知我
     3.循环等待
    
  • 源码分析入口:

     frameworks/base/cmds/app_process/Android.bp 
     frameworks/base/cmds/app_process/app_main.cpp
    
  • zygote启动时执行main方法

  • zygote作用:进入到java层

    • 在main方法中有AppRuntime,这个是继承自AndroidRuntime;

      • 那么android的运行时环境就是zygote启动的
  • 传入的参数:-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote

  • 参数解析过程:for循环,对于argv进行++,

    • 实现参数的不同进入不同的if分支结构,主要是通过设置标志位

      • 标志位

         zygote = true;
         startSystemServer = true;//zygote启动后会顺带启动systemserver
        
    • 启动运行时环境:

      • 代码

         //启动运行时环境
         if(zygote){
             //里面是参数哟
         runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
         }
         //app启动的时候走这个
         else if(className){
         }
        
      • 因为AppRuntime没有start方法,其实会走到AndroidRuntime.start方法

    • 在AndroidRuntime.start方法干了什么:zygote的 native 的启动:启动运行时环境

      1. startVM:启动虚拟机

        • 虚拟机初始化操作

          • heapSize:16MB,最开始这么大
      2. startReg:注册JNI

        • native层与java层是不能直接通信的,java代码要调用c代码,要桥

        • 注册JNI就是建桥

          • 将 java本地方法与native关联起来
          • nativeXXX
          • 注册涉及到静态注册(这里就是静态注册),动态注册
        • 注册过程中干了什么?APPruntime

          1. 创建线程
          2. 创建200个局部引用,作为作用域大小
      3. env->CallStaticVoidMethod(startClass, startMeth, strArray); --》 main@ZygoteInit.java

  • zygote(cow)的java启动:现在还是在zygote进程中,分析zygote.main方法

    • 技巧:分析源码时,判断在那一个进程中:去找fork
    • 干了什么?

      1. preload(bootTimingsTraceLog); // 预加载,加快进程的启动

        • zygote是写时拷贝

        • 提前就把东西准备好了,你来了直接用

          • 应用不用再重复搞事情,preloadResuorce,preloadClass
        • /system/etc:预加载类

          image-20220503145238826

        • 预加载资源:com.android.internal.R.xx

          • drawable;
          • attr.xml等
        • 预加载示意图:

          image-20220503153129760

      2. zygoteServer = new ZygoteServer(isPrimaryZygote);

        • 是一个socket,AMS由socket通知zygote去创建新的进程

        • 为什么是sokect,点进来

          • 创建socket
          • 在AMS中创建连接
        • 为什么不同Binder:socket更加方便

          • Binder是多线程的,在创建进程时fork容易出现死锁
        • 为什么会出现

          • fork复制当前进程,这个时候,锁可能会丢掉
          • 此时的进程有锁,复制了锁在但是解锁的代码丢了,就造成死锁了
      3. Runnable r = forkSystemServer // 启动 SystemServer 进程,AMS

        • SystemServer

          • AMS在这个里面初始化,wms.pms,pks
          • AMS就是这个里面的一块代码
      4. caller = zygoteServer.runSelectLoop(abiList); // 进入死循环,接收AMS传过来的消息

        • 为什么要进入死循环:

          • 进程不能退出啊

          • 要接收AMS的消息啊

问题:

  • android最多能有多少个进程?

    • 安卓后台进程限制标准是2个。很多手机软件关闭之后,会变为后台运行。同时手机程序运行过多也会造成手机死机等现象,限制后台程序运行数量很有必要。开发者模式中的相关选项是供技术人员进行机器调试用,一般是无法自定义保存,改动开发者选项中的相关设置可能会造成手机的某些功能无法正常工作,谨慎进行操作。

    • 后台运行的进程是那些隐含执行的程序,比如现在正在使用QQ,而之前已经打开了微信,可是没有退出,那么现在微信就是在后台运行的程序。进行数量的限制可以帮助节省RAM的空间,提升手机运行速度。

      启动例程时,Oracle不仅会分配SGA,还会启动后台进程;关闭例程时,Oracle不仅会释放SGA所占用的内存空间,而且还会释放后台进程所占用的Cpu和内存资源。Oracle提供了很多后台进程,在这介绍常用后台进程SMON、PMON、DBWR、LGWR、CKPT、ARCH。

  • 进程和虚拟机是什么关系,哪个大???

    • 虚拟机由进程启动
    • 虚拟机是一块代码:实现了内存管理的功能而已
    • 代码是运行在进程中的;
     zygote进程 ---》 startVm 启动虚拟机(进程里面)(虚拟机这块代码实现了这个功能:内存管理)
    
  • 进程:

    • 内核空间:native层的东西

    • 用户空间:

      • zygote就是在这个地方
      • 进程就是程序分配的一个内存单位而已
    • APP进程中有没有JVM:显然有

      • zygote通过Fork创建出APP进程

      • Fork就是复制而已

        • 这个就涉及到COW,

          • 只要你不改,那么大家都用一块;你要改,那我就给你拷贝一份;要改都是指复制你要改的那块内存
          • 节约空间
        • 内核空间是共享的,但是是指物理空间的共享

      • 每一个APP进程,都是有一个JVM

        • APP与systemserver都是由zygote孵化出来的

        • 一个APP挂了不影响其他的

        • 一个APP中的多个进程,都是由zygote孵化出来的

          • android中的进程都是由zygote孵化出来的
  • zygote 启动的时候APP还没有启动

    • init---> kthreadd---> zygote
    • zygote---> systemserver
    • 可以通过ppid找关系
  • APP都没有启动为什么要先创建JVM

    • 首先,要执行java代码就需要虚拟机;

    • 不创建JVM,zygote的 java层怎么进入?

    • zygote创建一个空的,那么APP启动的时候去启动 JVM

      • 这个就很慢了;本来就慢,算作优化
      • zygote想要提前搞一点
  • daivk虚拟机什么时候跑

    • 这里面的androidRuntime就是ART啊