进程启动《Android内核剖析》读书笔记 第9章 Framework的启动过程

在本文中,我们主要介绍进程启动的内容,自我感觉有个不错的建议和大家分享下

    对于Linux内核来说,其实Android系统就是一个一般的Linux应用程序而已,只是在init.rc中被设置为默许启动;

    

  1. Framework运行环境综述
    任何系统启动进程的实质都是要建立一套系统运行所需的环境;android系统的启动其实就是建立dalvik虚拟机运行所需的环境,整体如下:
    进程和启动
    1. android系统启动的第一个进程为zygote,意思为“受精卵”,因为接下来所有的Dalvik虚拟机进程都是通过它孵化出来的;该进程包含2个核心模块:
      1. Socket服务器:用于接收启动新的Dalvik进程的命令;
      2. Framework共享类与资源:所有Dalvik虚拟机进程都市需要的共享类及资源,其实就是android.jar中的大部分内容,待加载的类和资源列表分离通过preload-classes/preload-resources定义;
    2. zygote孵化出的第一个Dalvik虚拟机进程为SystemServer;该进程会负责加载android应用所需的所有基础服务(比如PowerManagerServiceAlarmManagerService、WifiService...),并且会创立一个由Ams(详细的货色详见前面的第19章说明)管理的Socket客户端,该客户端用于向zygote中的服务端发送启动新Dalvik进程命令;
    3. 从之前的运行环境架构图可以看出有如下特色:
      1. 在android系统中每一个应用就是一个独立的dalvik进程,他们之间相互独立,这样就保证了单个应用的crash不会影响其他应用;而且可以设置单个应用的资源和权限制约,比如内存等;
      2. 公共的类和资源只会加载一份,这样对应用而言只要加载自身所需的类和资源即可,这样就有效的节省了对内存的消耗、加载效率也会提升;
        这点和PC上运行多个JAVA应用是不一样的,在PC上一般不存在多个JAVA进程共享一份类和资源的情况;
  2. Zygote进程的启动进程
    1. 加载点为linux系统根目录下的init.rc中配置的zygote服务,该文件可以直接通过adb pull /init.rc .命令或者在DDMS视图中下载,对应于android源码中的路径为./system/core/rootdir/,详细配置为:
      service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server    class main
          socket zygote stream 660 root system
          onrestart write /sys/android_power/request_state wake
          onrestart write /sys/power/state on
          onrestart restart media
          onrestart restart netted
      该以上配置命令中核心信息包含:
      1. 用/system/bin/app_process命令启动名称为zygote的系统服务(注意:该进程没法在DDMS中直接查看,他属于系统服务);
        其作用可以简单理解为就是启动一个Dalvik虚拟机,并通过指定参数启动一个JAVA应用进程,和执行java命令实质上是一样的;
        该命令的源码位于./frameworks/base/cmds/app_process/app_main.cpp
        源码会依赖于./frameworks/base/include/android_runtime/AndroidRuntime.h
        详细实现位于./frameworks/base/core/jni/AndroidRuntime.cpp
        1. 启动Dalvik虚拟机的详细指令路径为:
          app_main.cpp中的main() ==> AndroidRuntime.cpp中的start() ==> AndroidRuntime.cpp中的startVm() 
          ==> ./dalvik/vm/Jni.cpp中的JNI_CreateJavaVM() ==> ./dalvik/vm/Init.cpp中的dvmStartup();
          期间会实现多个dalvik基础服务启动以及dalvik.system.NativeStart.main()的执行;
        2. Dalvik虚拟机环境预备终了以后,利用FindClass/CallStaticVoidMethod执行详细Java文件的main方法;
      2. --zygote标示以com.android.internal.os.ZygoteInit类为虚拟机执行入口;
      3. --start-system-server仅在指定--zygote参数下有效,标示ZygoteInit启动终了后孵化出第一个Dalvik进程SystemServer;
      4. socket标示zygote进程中启动的Socket服务端类型以及端口等信息;
      5. onrestart标示该服务重启后需要触发的操作,即需要唤醒电源、重启media/netted服务;
    2. ZygoteInit.main()核心加载逻辑
      1. registerZygoteSocket(); 构造LocalServerSocket启动Socket服务端,用以接收启动新的Dalvik进程的命令;
      2. preloadClasses(); 加载Framework共享类,待加载列表位于framework.jar/preloaded-classes文件;
        preloaded-classes源文件位于./frameworks/base/preloaded-classes;
        而该文件又可以通过java -Xss512M -cp ./out/host/darwin-x86/framework/preload.jar WritePreloadedClassFile ./frameworks/base/tools/preload/XXXXXX.compiled来生成;
        preload源码位于./frameworks/base/tools/preload/,最后一个参数XXXXXX.compiled二进制文件等于真正的待加载列表;而至于这个.compiled二进制文件如何来的,还未知!
      3. preloadResources(); 加载Framework共享资源,包含图片和颜色;待加载资源位于/frameworks/base/core/res/res/values/arrays.xml;
        其中定义了2个array,分离为preloaded_drawables/preloaded_color_state_lists;
      4. startSystemServer(); 孵化出第一个Dalvik虚拟机进程SystemServer;
        1. 指定新进程启动相关参数,主要是进程名称system_server(为什么DDMS中看到的进程名称为system_process呢?)、以及将要执行的第一个Java类:com.android.server.SystemServer
        2. 执行Zygote.forkSystemServer(…)创立新进程;
          该进程现实上是通过Linux系统的一个系统调用fork(); 实现的,其作用是复制当前进程所包含的所有信息,并发生一个新进程;这样做的目标主要是为了确保Zygote进程中加载的共享类和资源只会存在一份,从而有效节省系统资源;
        3. 执行handleSystemServerProcess(…);
          1. closeServerSocket(); 关闭从zygote进程中copy过去的Socket服务器;
          2. RuntimeInit.zygoteInit(…); 初始化一些额外的运行环境,并执行RuntimeInit.invokeStaticMain(…) 实现对SystemServer.main()函数的运行,至此就实现了新的Java进程SystemServer的创立了;
            而至于main()中执行的详细逻辑如下:
            1. 执行Looper.prepareMainLooper(); 为主线程做异步消息队列预备;
            2. 加载android应用所需的所有基础服务,比如:Ams、Wms、Pms、WifiService…
            3. 执行ActivityManagerService.self().systemReady(…); 
              1. 首先会执行 startSystemUi(…); 启动 com.android.systemui.SystemUIService服务(可以通过DDMS查看到com.android.systemui进程);
                该进程主要是实现顶部状态栏PhoneStatusBar、电池电量PowerUI、铃声RingtonePlayer这三个服务的加载;
                注意:该部分逻辑在老版本中可能没有,书中也没提到,我是以4.2版本中的源码为准的;
              2. 执行多个服务的systemReady()方法,因为很多服务需要等Ams初始化实现才能真正ready;
              3. 执行mMainStack.resumeTopActivityLocked(); 启动任务列表中的最上面的一个Activity;
                在startHomeActivityLocked()执行进程中,系统会查询所有能响应category为CATEGORY_HOME的Activity列表,若涌现多个则可以由用户自己选择,并允许用户记住该选择;
                至此android系统的主页面应当就加载出来了,包含系统状态栏、以及Home;
                从上可以看出,任何人都可以做一个Laucher应用,只要申请可以响应对应的category即可,就像Facebook做的Home应用一样;
            4. 执行 Looper.loop(); 主线程进入接受消息状态;
      5. runSelectLoopMode(); 让之前新建好的Socket服务端进入非阻塞读操作,等待接收命令;
        当接收到请求时会调用ZygoteConnection.runOnce(),该方法的核心作用就是孵化出新的应用进程;
        1. 详细创立新进程是靠执行Zygote.forkAndSpecialize(…) 实现的;与SystemServer进程的创立对比可以看出,两者的方式并不完全相同,但都是基于fork模式来复制进程;
        2. 对复制出来进程善后处置,这个和SystemServer新建后的处置分歧,主要是关闭Socket服务器、以及启动新的目标Java文件ActivityThread中的main函数;
    每日一道理
我把卷子摊在课桌上,恨不得敲一阵锣,叫大家都来看看我这光彩的分数。

    这个章节有非常多的细节待专研,目前的困惑包含:

    

  1. dalvik虚拟机启动时如何和dalvik.system.NativeStart关联上的?而且该class居然找不到对应的jni文件?
  2. startSystemUi时启动的是一个Service,但现实加载的顶部状态栏PhoneStatusBar却明显和前台UI相关,如何实现的前台UI可以不依赖于Activity?
  3. 系统默许启动后还会加载一些基础应用,比如电话phone、短信sms等,这些应用又是在什么时候自动启动的呢?

文章结束给大家分享下程序员的一些笑话语录: 关于编程语言
如果 C++是一把锤子的话,那么编程就会变成大手指头。
如果你找了一百万只猴子来敲打一百万个键盘,那么会有一只猴子会敲出一 段 Java 程序,而其余的只会敲出 Perl 程序。
一阵急促的敲门声,“谁啊!”,过了 5 分钟,门外传来“Java”。
如果说 Java 很不错是因为它可以运行在所有的操作系统上,那么就可以说 肛交很不错,因为其可以使用于所有的性别上。

原文地址:https://www.cnblogs.com/jiangu66/p/3084522.html