必备的系统知识#系统启动

25 阅读9分钟

必备的系统知识#系统启动

必备的系统知识#AMS

必备的系统知识#PMS

必备的系统知识#Binder

必备的系统知识#Apk打包签名

必备的系统知识#Handler

一.系统启动

1.启动架构图

2.简要流程分析

1.BootRom->BootLoader->Kernel->Init->Zygote->SystemServer->Launcher

  • 手机开机后,引导芯片启动,会运行固化在ROM中的预设代码,加载引导程序到Ram,BootLoader检查Ram,初始化硬件等参数;
  • 硬件初始化之后,会进入到Kenerl,主要加载一些硬件驱动,初始化进程管理的操作,启动Swapper进进程(pid=0),用于初始化进程管理,内存管理、加载驱动等操作,在启动的Kthread,它是内核进程的鼻祖;
  • kernel加载完后,硬件驱动和HAL层交互,初始化进程管理等操作启动INIT进程
  • init进程启动后,会启动adbd、logd等用户进程,并且启动servicemanager进程、mediaserver进程、surfaceFlinger进程、Zygote孵化进程
  • Zygote会加载虚拟机、预设资源、fork systemserver进程、开启serverSocket监听孵化请求等
  • SystemServer进程会启动各种服务,将服务的BBinder添加到ServerManager中去,然后启动Launcher,

当然Launcher进程也是通过和Zygote交互孵化出来的进程,在通过AMS、WMS等将界面展现出来。

二.Init进程

1. 什么是init进程?

当android系统启动时,系统会经过BootLoader->Kernel->内核启动的第一个用户空间的进程,它就是init进程,我们可以通过adb shell ps查看,它的进程号pid是1,它的ppid是0,这也印证是内核进程创建了它。

2. init启动都干了啥?

init进程实际上也是一段可执行程序,他的代码路径是/system/core/init/init.cpp,它
会做一些挂载创建文件等操作,然后去读取init.rc,并执行这个任务清单,我们来看看init.rc里面都有哪些任务清单,源码目录:/system/core/rootdir/init.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进程的脚本
...
...
on post-fs
    # Load properties from
    #     /system/build.prop,
    #     /odm/build.prop,
    #     /vendor/build.prop and
    #     /factory/factory.prop
    load_system_props
    # start essential services
    start logd
    start servicemanager //启动serviceManager进程,这个进程是用来管理各种系统服务的binder
    start hwservicemanager
    start vndservicemanager

3. init进程有什么用?

1.启动系统关键的服务(如打电话、网络、蓝牙、铃声等服务)
2.守护关键服务,如果其中一个关键服务被杀死,手机将会被重启
同样我们可以通过adb shell ps 通过查看进程id和ppid.可以看到他们之间的关系,
3.启动ServiceManager

我么也可以通过kill由init进程守护的进程,来看看是否他们会重启,
所以我们如果想实现一个系统服务不被杀死,最好的方式由init进程启动且称为关键服务。

二.Zygote服务的启动

1. zygote服务是怎么启动的?

由init进程启动,我们可以了解到,它启动时读取的init.rc脚本里面有一个就是启动zygote进程的执行代码init.{ro.zygote}.rc,{ro.zygote}理解它是一个占位符,它会根据系统类型分为32位和64位,我们来看看init.zygote.rc这个里面相关代码,代码路径:
/system/core/rootdir/

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc
    socket zygote 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

我们看第一行它首先执行/system/bin/app_process下面的main.c,并且传入相关参数,那么我们就来看看这个app_process这个代码,代码路径/frameworks/base/cmds/app_process/app_main.cpp

while (i < argc) {
    const char* arg = argv[i++];
    if (strcmp(arg, "--zygote") == 0) {
        zygote = true;
        niceName = ZYGOTE_NICE_NAME; //修改app_process进程名为Zygote
    }...
        }
...
    if (zygote) {
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);//执行AndroidRuntime.cpp的start方法
} 
...

我们可以看到原来Zygote进程本来叫app_process,是启动时改成了这个NICE_NAME。Zygote虽然是在Framework native中由C语言main入口执行的,但因为init进程是在用户空间,所以它也是在用户空间。
我们在来看看runtime.start(),这个里面干了什么,我们打开这段代码,他的目录是在/framework/base/core/jni/AndroidRuntime.cpp

JNIEnv* env;  //启动jvm
if (startVm(&mJavaVM, &env, zygote) != 0) {
    return;
}
onVmCreated(env);

/*
     * Register android functions.
     */
if (startReg(env) < 0) { //注册jni
    ALOGE("Unable to register all android natives\n");
    return;
}
...
    //开启java世界,反射执行ZygoteInit的main方法
    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 */
} 
...

通过上面的代码,我们可以看到,AndroidRuntime主要做了三件事情:
1.启动虚拟机(这里面可以对虚拟机进行调优)
2.动态注册java调用native中的jni(让我们可以在java层调用native方法)
3.反射调用ZygoteInit中的main方法(开启java世界)

2. ZygoteInit都在干什么?

我们通过上面知道,Zygote进程通过runtime开启了java世界,它执行了ZygoteInit中的main方法,我们来看看main方法中的代码,代码路径:/framework/base/core/java/com/android/internal/os/ZygoteInit

//创建serverSocket
ZygoteServer zygoteServer = new ZygoteServer();

...
//加载系统类、系统资源类
if (!enableLazyPreload) {
    bootTimingsTraceLog.traceBegin("ZygotePreload");
    EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                        SystemClock.uptimeMillis());
    preload(bootTimingsTraceLog);
    EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                        SystemClock.uptimeMillis());
    bootTimingsTraceLog.traceEnd(); // ZygotePreload
}
    ...
//创建systemserver
if (startSystemServer) {
    startSystemServer(abiList, socketName, zygoteServer);
}
    ...
//开启ZygoteServerScoket循环,等待接收socket接收fork app进程的消息,没有消息就会阻塞休眠等待,为什么要循环?不循环进程就结束了额!!!
zygoteServer.runSelectLoop(abiList);

通过上面的代码,我们可以知道,main入口方法也主要做了三件事情。

1.预加载系统资源,如系统类、资源、系统共享库等

预加载的好处就是不需要每个app进程都去加载这个资源类,直接使用这些提前预加载好的类和资源

2.创建ZygoteServer,也就是ServerSocket,循环等待通知fork子进程!

首先我们要理解fork就是复制,Zygote fork进程就是在Zygote的基础上复制一个子进程,子进程拥有父进程处理好的资源。

3.创建SystemServer进程

SystemServer进程是Zygote创建的第一个子进程!!!

三.SystemServer进程

由上面我们知道SystemServer他是由Zygote进程通过启动Zygote进程通过ZygoteInit中的main方法调用startSystemServer启动的,我们来看看它的代码!

public static void main(String[] args) {
    new SystemServer().run();
}

...
private void run(){
      //->>>1.初始化SYstem Context
      createSystemContext();

    //system_server进程启动服务管理类
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    ...
    //启动引导服务
    startBootstrapServices();
    //启动核心服务
    startCoreServices();
    //启动其他服务
    startOtherServices()
    ...
}
...
private void startBootstrapServices() {

    // 启动ams服务
    mActivityManagerService = mSystemServiceManager.startService(
        ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
    //启动pms服务
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                                                        mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    mFirstBoot = mPackageManagerService.isFirstBoot();  
    ...
}

...

通过代码我们可以看到SystemServer其实就是启动一些引导服务、核心服务和其他服务,这些服务都运行在system_server进程中,而且这些服务也都运行在这个进程中,由于这些服务较多,所以用SystemServiceManager来管理这些服务。

1.孵化server进程简单梳理

孵化主要是通过native层进行,最终经过一些操作后反射执行SystemServer的main方法

2.服务启动简单分析

我们至少需要理清楚它的8个阶段,这个定义在SystemService代码中,源码路径是:base\services\core\java\com\android\server\SystemService.java,并且当阶段改变时,也会回调对应的service中的方法onBootPhase方法,这个是由SystemServiceManager中startBootPhase方法来调用,代码路径为:\services\core\java\com\android\server\SystemServiceManager.java

其中PHASE_BOOT_COMPLETED=1000,该阶段是发生在Boot完成和home应用启动完毕。系统服务更倾向于监听该阶段,而不是注册广播ACTION_BOOT_COMPLETED,从而降低系统延迟。

四.问题思考

1.Zygote进程的原始进程是什么?

原始进程叫app_process,它是在init启动时被启动的,在app_main.c被修改的

2.Zygote是在内核空间还是在用户空间?

当然是在用户空间拉,它是有init进程启动,而init也是在用户空间。

3.app进程的启动,为什么是从Zygote fork,而不是有init进程fork?

Zygote创建到启动,做了很多事情,比如创建虚拟机,注册jni,预加载等,fork进程就是复制,如果不在Zygote,那么创建新进程就要对上面的流程重新走一遍,如果从
Zygote去fork,app进程可以使用父进程的相关资源,不需要在处理。而init进程主要做的是挂在文件、解析init.rc脚本,启动其他进程。

4.Zygote为什么用socket而不是binder?

原因1:并发问题。假如使用binder,由于binder支持多线程,就有个并发问题,并发问题的处理方式就是加锁,加锁可能导致死锁,为什么会出现死锁?比如ams使用binder告知Zygote去fork一个app进程,为了保证不发生并发问题,就会获取对方的binder代理并加锁,Zygote去fork应用进程时把binder的锁状态也复制进去了,那么app进程的binder的锁谁来解?子进程没有解锁就会出现死锁!

原因2:时序问题。安卓中一般使用的binder引用,都是保存在ServiceManager进程中的,而如果想从ServiceManager中获取到对应的binder引用,前提是需要注册,而注册的行为是在对应的逻辑代码执行时才会去注册的。流程上,是Init产生Zygote进程和ServiceManager进程,然后Zygote进程产生SystemServer进程。如果AMS想通过binder向Zygote发送信号,必须向ServiceManager获取Zygote的binder引用,而前提是Zygote进程中必须提前注册好才行。而实际上,虽然Init进程是先创建ServiceManager,后创建Zygote进程的。虽然Zygote更晚创建,但是也不能保证Zygote进程去注册binder的时候,ServiceManager已经初始化好了。当然有人说,等ServiceManager完全初始化好再去注册不就好了吗?可当然可以了,但是什么时候才能可以注册呢?这个时间点无法保证,如果想保证就必须通过另外一种跨进程通讯的方式来保证,这样设计不就变得复杂了吗?还有人说,我Zygote启动后,延时10秒或者20秒再去向ServiceManager进程注册不就好了吗?这样做自然也是可以的,但是有没有考虑下,如果用户恰好在这期间点击了应用图标尝试去启动APP应该怎么办呢?岂不就是没有反应了吗?所以,AMS无法获取到Zygote的binder引用,是原因之一。

5.ServiceManager和SystemServiceManager的关系?

ServiceManager是一个独立的进程,它和Zygote一样,都是有init进程通过执行init.rc的脚本去启动的,它是管理system_server进程中各种服务binder的,而SysteServiceManager则是SystemerServer中对启动服务的集中管理。