Android Framework系列(从系统开机到应用启动篇)

8 篇文章 1 订阅

前言:Android手机从系统开机到应用启动,里面程涉及到的Framework层、内核层知识还是挺多的,作为一名开发者,熟悉常见的通信方式(Socket、Binder、Handler等)、了解Android系统的整体架构势在必行。本篇文章带领大家一起来领略Android系统从开机到应用启动的前世今生。

整体大纲:
在这里插入图片描述

一、Launcher进程的启动过程

主要分为以下几个步骤:

  1. Linux内核启动
  2. init进程启动
  3. init进程fork出Zygote进程
  4. Zygote进程fork出SystemServer进程
  5. SystemServer进程启动各项服务(PMS、AMS等)
  6. AMS服务启动Launcher桌面
    在这里插入图片描述
1. Linux内核启动

从boot loader跳转到linux kernel层,执行第一条指令。

2. init进程启动

init进程是Linux系统中用户空间的第一个进程,进程号固定为1。Kernel启动后,在用户空间启动init进程,并调用init中的main()方法执行init进程的职责。

3. Zytote进程的启动过程

在这里插入图片描述
总结:init进程读取配置文件init.rc后,fork出Zygote进程,通过execve函数执行Zygote的执行程序app_process,进入ZygoteInit类的main函数。

  1. init进程读取init.rc文件
 //system/core/rootdir/init.rc
  import /init.${ro.zygote}.rc

${ro.zygote}的取值有4种,在init.rc的同级目录/system/core/rootdir下,可以看到4个Zygote相关的配置文件,表示系统所支持程序的bit位数,

  • init.zygote32.rc,Zygote进程的执行程序路径为/system/bin/app_process
  • init.zygote64.rc,Zygote进程的执行程序路径为/system/bin/app_process64
  • init.zygote32_64.rc,会启动两个Zygote进程,有两个执行程序,32为主模式
  • init.zygote64_32.rc,会启动两个Zygote进程,有两个执行程序,64为主模式
  1. init.zygote32.rc配置文件:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    ......

第一行中:

  • service表示Zygote进程以服务的形式来启动
  • zygote则是进程的名字,/system/bin/app_process是执行程序的路径,
  • start-system-server表示在Zygote进程启动后需要启动SystemServer进程。

第六行中:

  • Zygote进程是使用socket来进行跨进程通信的,所以会创建一个名为zygote的socket,
  • 660表示访问权限rw-rw----,表示文件拥有者和同一群组用户具有读写权限。
  1. 通过fork出zygote进程,再通过execve方法来执行zygote的执行程序app_proces.main(),然后AndroidRuntime.start() ->ZygoteInit.main() 至此Zygote就正式启动了:
//system/core/init/service.cpp
bool Service::Start() {
    //fork出子进程
    pid = fork();
    if (pid == 0) {//子进程会返回0,父进程会返回子进程的pid
        //strs[0]是执行程序的路径,即execve会运行app_process
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
        }
    }
}
//frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]){
    if (zygote) {
        //启动Zygote,进入ZygoteInit.main函数,执行AndroidRuntime.start()方法
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    }
}
//frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(...){
    //1. 启动java虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    //2. 为java虚拟机注册JNI方法
    if (startReg(env) < 0) {
        return;
    }
    //根据传入的参数找到ZygoteInit类和他的main函数
    //3. 通过JNI调用ZygoteInit的main函数
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
//ZygoteInit.java
public static void main(String argv[]) {
    //是否要创建SystemServer
    boolean startSystemServer = false;
    //默认的socket名字
    String socketName = "zygote";
    //是否要延迟资源的预加载
    boolean enableLazyPreload = false;

    for (int i = 1; i < argv.length; i++) {
        if ("start-system-server".equals(argv[i])) {
            //在init.rc文件中,有--start-system-server参数,表示要创建SystemServer
            startSystemServer = true;
        } else if ("--enable-lazy-preload".equals(argv[i])) {
            //init.rc没有这个参数,资源的预加载不会被延迟
            enableLazyPreload = true;
        } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
            //init.rc可以通过--socket-name=指定socket名字来覆盖默认值
            socketName = argv[i].substring(SOCKET_NAME_ARG.length());
        }
    }

    //1. 创建服务端socket,名字为socketName即zygote
    zygoteServer.registerServerSocket(socketName);

    if (!enableLazyPreload) {
        //2. 没有被延迟,就预加载资源
        preload(bootTimingsTraceLog);
    }

    if (startSystemServer) {
        //3. fork并启动SystemServer进程
        startSystemServer(abiList, socketName, zygoteServer);
    }

    //4. 等待AMS请求(AMS会通过socket请求Zygote来创建应用程序进程)
    zygoteServer.runSelectLoop(abiList);
}
4. SystemServer的启动过程

在这里插入图片描述
总结:SystemServer进程被创建后,主要做了3件事情:启动binder线程池、创建SystemServiceManager(SSM)、用SSM启动各种服务。

  1. 这里启动了binder线程池,SystemServer进程就可以用binder机制来跨进程通信了(Zygote进程是用socket来通信的),接着进入了SystemServer的main函数:
//ZygoteInit.java
private static boolean startSystemServer(...){
    String args[] = {
        //...
        //启动的类名:
        "com.android.server.SystemServer",
    };
    //fork进程,由native层实现
    pid = Zygote.forkSystemServer();
    //处理SystemServer进程
    handleSystemServerProcess(parsedArgs);
}

private static void handleSystemServerProcess(...){
    ZygoteInit.zygoteInit(...);
}

public static final void zygoteInit(...){
    //启动binder线程池
    ZygoteInit.nativeZygoteInit();
    //内部经过层层调用,找到"com.android.server.SystemServer"类和他的main函数,然后执行
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
//SystemServer.java
public static void main(String[] args) {
    new SystemServer().run();
}

private void run() {
    //创建looper
    Looper.prepareMainLooper();
    //加载动态库libandroid_servers.so
    System.loadLibrary("android_servers");
    //创建系统上下文
    createSystemContext();

    //创建SSM,用于服务的创建、启动和生命周期管理
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    
    //服务根据优先级被分成3批来启动:
    //启动引导服务,如AMS、PMS等
    startBootstrapServices();
    //启动核心服务
    startCoreServices();
    //启动其他服务
    startOtherServices();

    //开启looper循环
    Looper.loop();
}
//SystemServer.java
private void startBootstrapServices() {
    //由SSM创建启动
    mActivityManagerService = mSystemServiceManager.startService(
        ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
}

private void startOtherServices() {
    //AMS准备就绪
    mActivityManagerService.systemReady(...);
}
5. Launcher的启动过程

在这里插入图片描述
总结:Launcher作为Android的桌面,用于管理应用图标和桌面组件。SystemServer进程会启动各种服务,其中PackageManagerService启动后会将系统中的应用程序安装完成,然后由AMS来启动Launcher。

  1. SystemServer的startOtherServices()方法中启动了AMS服务,然后调用了AMS的systemReady()方法,最后来到了startHomeActivityLocked(),启动的Activity类是Launcher.java,剩下的流程就是加载已安装的应用程序信息。**
//SystemServer.java
private void startOtherServices() {
    //AMS准备就绪
    mActivityManagerService.systemReady(...);
}
//ActivityManagerService.java
public void systemReady(...) {
    //经过层层调用来到startHomeActivityLocked
    startHomeActivityLocked(...);
}
//ActivityManagerService.java
boolean startHomeActivityLocked(...) {
    //最终会启动Launcher应用的Activity
    mActivityStarter.startHomeActivityLocked(...);
}

二、Launcher启动应用App的过程

主要分为以下几个步骤:

  • AMS发送socket请求
  • Zygote处理socket请求
  • Zygote进程fork出应用进程,应用进程继承得到虚拟机实例
  • 应用进程启动binder线程池、运行ActivityThread类的main函数、启动Looper循环
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701003057851.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTM3NjkyNzQ=,size_16,color_FFFFFF,t_70
1. AMS发送socket请求

Android应用进程的启动是被动式的,在Launcher桌面点击图标启动一个应用的组件如Activity时,如果Activity所在的进程不存在,就会创建并启动进程。点击App图标后经过层层调用会来到ActivityStackSupervisor的startSpecificActivityLocked方法:

//ActivityStackSupervisor.java
final ActivityManagerService mService;

void startSpecificActivityLocked(...) {
    //查找Activity所在的进程,ProcessRecord是用来封装进程信息的数据结构
    ProcessRecord app = mService.getProcessRecordLocked(...);
	//如果进程已启动,并且binder句柄IApplicationThread也拿到了,那就直接启动Activity
    if (app != null && app.thread != null) {
        realStartActivityLocked(r, app, andResume, checkConfig);
        return;
    }
	//否则,让AMS启动进程
    mService.startProcessLocked(...);
}
  • app.thread并不是线程,而是一个binder句柄。应用进程使用AMS需要拿到AMS的句柄IActivityManager,而系统需要通知应用和管理应用的生命周期,所以也需要持有应用进程的binder句柄IApplicationThread。
    也就是说,他们互相持有彼此的binder句柄,来实现双向通信:

在这里插入图片描述
那IApplicationThread句柄是怎么传给AMS的呢?Zygote进程收到socket请求后会处理请求参数,执行ActivityThread的入口函数main:

//ActivityThread.java
public static void main(String[] args) {
    //创建主线程的looper
    Looper.prepareMainLooper();
    //ActivityThread并不是线程,只是普通的java对象
    ActivityThread thread = new ActivityThread();
    //告诉AMS,应用已经启动好了
    thread.attach(false);
	//运行looper,启动消息循环
    Looper.loop();
}

private void attach(boolean system) {
    //获取AMS的binder句柄IActivityManager
    final IActivityManager mgr = ActivityManager.getService();
    //告诉AMS应用进程已经启动,并传入应用进程自己的binder句柄IApplicationThread
    mgr.attachApplication(mAppThread);
}

所以对于AMS来说,AMS向Zygote发起启动应用的socket请求,Zygote收到请求fork出进程,返回进程的pid给AMS;应用进程启动好后,执行入口main函数,通过attachApplication方法告诉AMS已经启动,同时传入应用进程的binder句柄IApplicationThread。完成这两步,应用进程的启动过程才算完成。

下面看AMS的startProcessLocked启动应用进程时都做了些什么?

//ActivityManagerService.java
final ProcessRecord startProcessLocked(...){
    ProcessRecord app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
    //如果进程信息不为空,并且已经拿到了Zygote进程返回的应用进程pid
    //说明AMS已经请求过了,并且Zygote已经响应请求然后fork出进程了
    if (app != null && app.pid > 0) {
        //但是app.thread还是空,说明应用进程还没来得及注册自己的binder句柄给AMS
        //即此时进程正在启动,那就直接返回,避免重复创建
        if (app.thread == null) {
            return app;
        }
    }
    //调用重载方法
    startProcessLocked(...);
}

之所以要判断app.thread,是为了避免当应用进程正在启动的时候,假如又有另一个组件需要启动,导致重复拉起(创建)应用进程。

继续看重载方法startProcessLocked,

//ActivityManagerService.java
private final void startProcessLocked(...){
    //应用进程的主线程的类名
    if (entryPoint == null) entryPoint = "android.app.ActivityThread";
    ProcessStartResult startResult = Process.start(entryPoint, ...);
}

//Process.java
public static final ProcessStartResult start(...){
    return zygoteProcess.start(...);
}

来到ZygoteProcess,

//ZygoteProcess.java
public final Process.ProcessStartResult start(...){
    return startViaZygote(...);
}

private Process.ProcessStartResult startViaZygote(...){
    ArrayList<String> argsForZygote = new ArrayList<String>();
    //...处理各种参数
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}

其中:

  1. openZygoteSocketIfNeeded打开本地socket
  2. zygoteSendArgsAndGetResult发送请求参数,其中带上了ActivityThread类名
  3. return返回的数据结构ProcessStartResult中会有pid字段

整体梳理一下:
在这里插入图片描述

2. Zygote处理socket请求

在ZygoteInit的main函数中,会创建服务端socket,

 //ZygoteInit.java
public static void main(String argv[]) {
    //Server类,封装了socket
    ZygoteServer zygoteServer = new ZygoteServer();
    //创建服务端socket,名字为socketName即zygote
    zygoteServer.registerServerSocket(socketName);
    //进入死循环,等待AMS发请求过来
    zygoteServer.runSelectLoop(abiList);
//ZygoteServer.java
void registerServerSocket(String socketName) {
    int fileDesc;
    //socket真正的名字被加了个前缀,即 "ANDROID_SOCKET_" + "zygote"
    final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

    String env = System.getenv(fullSocketName);
    fileDesc = Integer.parseInt(env);

    //创建文件描述符fd
    FileDescriptor fd = new FileDescriptor();
    fd.setInt$(fileDesc);
    //创建LocalServerSocket对象
    mServerSocket = new LocalServerSocket(fd);
}

void runSelectLoop(String abiList){
    //进入死循环
    while (true) {
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if (i == 0) {
                //...
            } else {
                //得到一个连接对象ZygoteConnection,调用他的runOnce
                boolean done = peers.get(i).runOnce(this);
            }
        }
    }
}
//ZygoteConnection.java
boolean runOnce(ZygoteServer zygoteServer){
    //读取socket请求的参数列表
    String args[] = readArgumentList();
    //创建应用进程
    int pid = Zygote.forkAndSpecialize(...);
    if (pid == 0) {
        //如果是应用进程(Zygote fork出来的子进程),处理请求参数
        handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
        return true;
    } else {
        return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
    }
}

handleChildProc方法调用了ZygoteInit的zygoteInit方法,里边主要做了3件事:

  1. 启动binder线程池(后面分析)
  2. 读取请求参数拿到ActivityThread类并执行他的main函数,执行thread.attach告知AMS并回传自己的binder句柄
  3. 执行Looper.loop()启动消息循环(代码前面有)

这样应用进程就启动起来了。梳理一下:

>

3.启动binder线程池

Zygote的跨进程通信没有使用binder,而是socket,所以应用进程的binder机制不是继承而来,而是进程创建后自己启动的。前边可知,Zygote收到socket请求后会得到一个ZygoteConnection,他的runOnce会调用handleChildProc,

//ZygoteConnection.java
private void handleChildProc(...){
    ZygoteInit.zygoteInit(...);
}

//ZygoteInit.java
public static final void zygoteInit(...){
    RuntimeInit.commonInit();
    //进入native层
    ZygoteInit.nativeZygoteInit();
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
//AndroidRuntime.cpp
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz){
    gCurRuntime->onZygoteInit();
}
//app_main.cpp
virtual void onZygoteInit()
{
    //获取单例
    sp<ProcessState> proc = ProcessState::self();
    //在这里启动了binder线程池
    proc->startThreadPool();
}
//ProcessState.cpp
sp<ProcessState> ProcessState::self()
{
    //单例模式,返回ProcessState对象
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState("/dev/binder");
    return gProcess;
}

//ProcessState构造函数
ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
        , mDriverFD(open_driver(driver)) //打开binder驱动
        ,//...
{
    if (mDriverFD >= 0) {
        //mmap是一种内存映射文件的方法,把mDriverFD映射到当前的内存空间
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, 
                        MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
    }
}

//启动了binder线程池
void ProcessState::startThreadPool()
{
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        //创建线程名字"Binder:${pid}_${自增数字}"
        String8 name = makeBinderThreadName();
        sp<Thread> t = new PoolThread(isMain);
        //运行binder线程
        t->run(name.string());
    }
}

ProcessState.cpp文件里面有两个宏定义值得注意一下(感兴趣的可以看下这个链接):

  • 一次Binder通信最大可以传输的大小是 1MB-4KB*2
  • #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
  • binder驱动的文件描述符fd被限制了最大线程数15
  • #define DEFAULT_MAX_BINDER_THREADS 15

我们看下binder线程PoolThread长啥样,

class PoolThread : public Thread
{
public:
    explicit PoolThread(bool isMain)
        : mIsMain(isMain){}
protected:
    virtual bool threadLoop()
    {	//把binder线程注册进binder驱动程序的线程池中
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }
    
    const bool mIsMain;
};
//IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain)
{
    //向binder驱动写数据:进入死循环
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    status_t result;
    do {
        //进入死循环,等待指令的到来
        result = getAndExecuteCommand();
    } while (result != -ECONNREFUSED && result != -EBADF);
    //向binder驱动写数据:退出死循环
    mOut.writeInt32(BC_EXIT_LOOPER);
}

status_t IPCThreadState::getAndExecuteCommand()
{
    //从binder驱动读数据,得到指令
    cmd = mIn.readInt32();
    //执行指令
    result = executeCommand(cmd);
    return result;
}

梳理一下binder的启动过程:

  • 打开binder驱动
  • 映射内存,分配缓冲区
  • 运行binder线程,进入死循环,等待指令

三、 走入Binder

1. 什么是Binder驱动

Binder驱动是Android专用的,但底层的驱动架构与Linux驱动一样。binder驱动在以misc设备进行注册,作为虚拟字符设备,没有直接操作硬件,只是对设备内存的处理。主要是驱动设备的初始化(binder_init),打开 (binder_open),映射(binder_mmap),数据操作(binder_ioctl)。

在这里插入图片描述

2. Binder通信原理

Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager以及binder驱动,其中ServiceManager用于管理系统中的各种服务。架构图如下所示:

在这里插入图片描述
可以看出无论是注册服务和获取服务的过程都需要ServiceManager,需要注意的是此处的Service Manager是指Native层的ServiceManager(C++),并非指framework层的ServiceManager(Java)。ServiceManager是整个Binder通信机制的大管家,是Android进程间通信机制Binder的守护进程,要掌握Binder机制,首先需要了解系统是如何首次启动Service Manager。当Service Manager启动之后,Client端和Server端通信时都需要先获取Service Manager接口,才能开始通信服务。

图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。

  • 注册服务(addService):Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。
  • 获取服务(getService):Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。
  • 使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:client是客户端,server是服务端。

图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自定义实现client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。

3. Binder内存机制

在这里插入图片描述
虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。当Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据copy_from_user拷贝到内核空间,而Server端(作为数据接收端)与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量,即可获悉内存地址,整个过程只发生一次内存拷贝。一般地做法,需要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程空间,会发生两次拷贝。

对于进程和内核虚拟地址映射到同一个物理内存的操作是发生在数据接收端,而数据发送端还是需要将用户态的数据复制到内核态。到此,可能有读者会好奇,为何不直接让发送端和接收端直接映射到同一个物理空间,那样就连一次复制的操作都不需要了,0次复制操作那就与Linux标准内核的共享内存的IPC机制没有区别了,对于共享内存虽然效率高,但是对于多进程的同步问题比较复杂,而管道/消息队列等IPC需要复制2两次,效率较低。这里就不先展开讨论Linux现有的各种IPC机制跟Binder的详细对比,总之Android选择Binder的基于速度和安全性的考虑。

下面这图是从Binder在进程间数据通信的流程图,从图中更能明了Binder的内存转移关系。

在这里插入图片描述

五、常见问题解答

  • Zygote的跨进程通信没有使用binder,而是socket,所以应用程序进程的binder机制不是继承而来,而是进程创建后自己启动的。
  • Zygote跨进程通信之所以用socket而不是binder,是因为binder通信是多线程的,而Zygote需要在单线程状态下fork子进程来避免死锁问题。
  • PMS、AMS等系统服务启动后会调用ServiceManager.addService()注册,然后运行在自己的工作线程。

参考文章:

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值