Android底层启动解析

过去的这一年多里,做过VR,AR,现在又回到了Android客户端,开始做AI相关的应用,Android方面的知识也开始重新捡了起来,所以在这儿撸一遍Android的底层源码,巩固加强一下在自己Android方面的技能。

在撸Android的源码之前,我们先要有两件事要准备,第一件事是有一份Android的源码,我一般是通过线上(http://androidxref.com/)查看源码的,这个网站上有从2.3到8.0的所有源码,第二件事就是鸟瞰一遍Android的系统架构。

Android底层启动解析_第1张图片
这是一张官方给出的系统架构图,通过上图我们可以知道,Android底层是Linux内核,通过Android RunTime和一些Libraries的支撑,运行Framework层,我们的app应用便是在FrameWork层上开发。Android的源码是庞大而复杂的,为了能体系的看完Android源码,我开始从最底层一点点的寻迹向上的过一遍,抛开Linux源码,我们第一步需要了解的是Linxu内核是如何启动Android系统的

我们先对启动流程有一个大致的了解,如下

  • init进程 –> Zygote进程 –> SystemServer进程 –>各种应用进程

所以要了解Android底层是如何启动的,我们就得先从Init进程开始,下面就来讲讲这个进程

Init进程

通过ps |grep 指令,我们可以看到init进程的进程号为1,它是Linux内核启动的第一个用户级进程,下面我们就来看看init进程的源码
这里写图片描述

init进程的源码位于/system/core/init/init.cpp,这是一个C++文件,该代码所做的主要有六件事情

  1. 挂载了tmpfs,devpts,proc,sysfs这4类文件系统
  2. 屏蔽标准的输入输出,并且初始化android自己的log系统
  3. 初始化Android系统所需的属性值
  4. 创建SELINUX(一种安全系统)
  5. epoll一个套接字fd,当init初始化完成后,变成一个守护进程,这个fd便可以循环监听事件
  6. 初始化SIGCHLD信号处理
  7. 解析init.rc,初始化zygote、audioserver、cameraserver、media、netd、wificond,并将解析的数据通过socket传送,后面的循环中会处理解析出来的action
  8. 当Init进程处理完,它就会进入一个循环化身为守护进程,处理signal、property和keychord等服务

对init所做的事情有大致的了解后我们直接来看看main函数的源码

int main(int argc, char** argv) {

//————————————————————————————第1部分——————————————————————————————————————————————
    /*
    /ueventd主要是负责建立Anroid中设备节点文件、权限设定等一些列工作。服务通过使用uevent,监控驱动发送的消息,做进一步处理
    /设备节点文件是设备驱动的逻辑文件,应用程序使用设备节点文件来访问驱动程序,我们添加设备,如U盘等,都会响应到ueventd进程,关于ueventd进程,之后在细说,在这儿大概了解,并不影响主体流程
    */
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }
//————————————————————————————第2部分——————————————————————————————————————————————

    /*
    /Watchdog是一个查错进程,时时去检查系统是否出错,如果出错会杀掉zygote进程,让系统重启
    */
    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }
//————————————————————————————第3部分——————————————————————————————————————————————
    // 设置允许当前进程创建文件或者目录最大可操作的权限
    umask(0);

    add_environment("PATH", _PATH_DEFPATH);
//————————————————————————————第4部分——————————————————————————————————————————————

    /* 挂载了tmpfs,devpts,proc,sysfs这4类文件系统
    /①tmpfs是一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中,如果你将tmpfs文件系统卸载后,那么其下的所有的内容将不复存在。tmpfs既可以使用RAM,也可以使用交换分区,会根据你的实际需要而改变大小。tmpfs的速度非常惊人,毕竟它是驻留在RAM中的,即使用了交换分区,性能仍然非常卓越。由于tmpfs是驻留在RAM的,因此它的内容是不持久的。断电后,tmpfs的内容就消失了,这也是被称作tmpfs的根本原因。
    /②devpts文件系统为伪终端提供了一个标准接口,它的标准挂接点是/dev/ pts。只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文件。
    /③proc文件系统是一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。
    */
    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
    if (is_first_stage) {
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    }

//————————————————————————————第5部分——————————————————————————————————————————————
    // 屏蔽标准的输入输出,并且初始化android自己的log系统
    open_devnull_stdio();
    klog_init();
    klog_set_level(KLOG_NOTICE_LEVEL);
    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
//————————————————————————————第6部分——————————————————————————————————————————————
    /*下面这段代码主要是初始化Android系统所需的属性,属性包括设备名字,蓝牙名字,编译信息,网络dns地址,以及其他的一些基本信息等,初始化的方式就是创建一个tmfs文件系统文件/dev/properties
    /property_init初始化属性域。在Android平台中,为了让运行中的所有进程共享系统运行时所需要的各种设置值,系统开辟了属性存储区域,并提供了访问该区域的API。
    /process_kernel_dt初始化DT(Device Tree)的属性集,即访问“/proc/device-tree/firmware/android ”目录,先看compatible文件内容是否是“android,firmware”,然后这个目录下每个文件名作为属性,文件里面的内容作为属性值。
    /process_kernel_cmdline处理内核命令行,读取“/proc/cmdline”文件中的内容,然后调用import_kernel_nv()函数设置系统属性,此处只将“/proc/cmdline”中前缀为“androidboot.”的值设置到属性中。DT属性集优先级高于此处设置的属性。
    /export_kernel_boot_props设置内核启动的变量,根据系统已有的属性设置,如有值则保持不变,否则设置成初始值。
    */
    if (!is_first_stage) {
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
        property_init();
        process_kernel_dt();
        process_kernel_cmdline();
        export_kernel_boot_props();
    }
//————————————————————————————第7部分——————————————————————————————————————————————
    /* 创建SELINUX(SELinux(Security-Enhanced Linux) 是美国国家安全局(NSA)对于强制访问控制的实现,是 Linux历史上最杰出的新安全子系统)
    /  restorecon命令用来恢复SELinux文件属性即恢复文件的安全上下文
    */
    selinux_initialize(is_first_stage);
    if (is_first_stage) {
        if (restorecon("/init") == -1) {
            ERROR("restorecon failed: %s\n", strerror(errno));
            security_failure();
        }
        char* path = argv[0];
        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
        if (execv(path, args) == -1) {
            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
            security_failure();
        }
    }
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon("/property_contexts");
    restorecon_recursive("/sys");
//————————————————————————————第8部分——————————————————————————————————————————————
    /*epoll一个fd,这个fd是一个通信套接字,当init初始化完毕后,为成为一个不中断for循环进程,即守护进程,这个fd便不断的监听信号,我们将这个fd看成是socket会更容易理解一点
    / epoll是Linux内核为处理大批量文件描述符而作了改进的poll,对epoll不清楚的可以查查相关资料
    /*/
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        ERROR("epoll_create1 failed: %s\n", strerror(errno));
        exit(1);
    }
//————————————————————————————第9部分——————————————————————————————————————————————
    /*
    /在Android中,当一个进程退出(exit())时,会向它的父进程发送一个SIGCHLD信号。父进程收到该信号后,会释放分配给该子进程的系统资源;并且父进程需要调用wait()或waitpid()等待子进程结束。如果父进程没有做这种处理,且父进程初始化时也没有调用signal(SIGCHLD, SIG_IGN)来显示忽略对SIGCHLD的处理,这时子进程将一直保持当前的退出状态,不会完全退出。这样的子进程不能被调度,所做的只是在进程列表中占据一个位置,保存了该进程的PID、终止状态、CPU使用时间等信息
    /所以这个方法是初始化SIGCHLD信号处理,SIGCHLD也是通过上面创建的fd来接收处理的,具体的初始化我们先略过,之后再详说
    */
    signal_handler_init();
//————————————————————————————第10部分——————————————————————————————————————————————
    //加载Android系统根目录的default.prop文件,该文件中包含了一些重要属性,在前面的代码中已经进行过属性初始化工作,这儿把文件中的属性值解析并发布到系统中
    property_load_boot_defaults();
    export_oem_lock_status();
    start_property_service();

//————————————————————————————第11部分——————————————————————————————————————————————
    //从这里起,便开始了对init.rc文件的解析和触发工作,init.rc是初始化工作的配置文件,以Android Init Language作为语法
    /关于init.rc的具体情况,可以参考http://qiangbo.space/2017-01-28/AndroidAnatomy_Init/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io这篇博客
    /我们在这里只需要知道zygote的启动便是配置在init.rc文件中的,zygote、audioserver、cameraserver、media、netd、wificond等底层server都会在这儿被解析出来,并将解析的数据通过socket传输,后面的for循环中会处理解析出来的action,关于init.rc的解析和触发,之后在详讲

    //这里将Action的function_map_替换为BuiltinFunctionMap
    //下文将通过BuiltinFuntionMap的map方法,获取keyword对应的处理函数
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    //构造出解析文件用的parser对象
    Parser& parser = Parser::GetInstance();
    parser.AddSectionParser("service",std::make_unique());
    parser.AddSectionParser("on", std::make_unique());
    parser.AddSectionParser("import", std::make_unique());
    //开始实际的解析过程
    parser.ParseConfig("/init.rc");

    // 获取ActionManager对象,需要通过am对命令执行顺序进行控制
    ActionManager& am = ActionManager::GetInstance();

    // 添加触发器early-init,执行on early-init内容
    am.QueueEventTrigger("early-init");
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // 添加触发器init,执行on init内容,主要包括创建/挂在一些目录,以及symlink等
    am.QueueEventTrigger("init");
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

    std::string bootmode = property_get("ro.bootmode");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
//————————————————————————————第12部分——————————————————————————————————————————————

    /*处理添加到运行队列的事件,当Init进程处理完,它就会进入一个循环化身为守护进程,处理socket接收到的服务
    /当while循环不断调用ExecuteOneCommand函数时,将按照trigger表的顺序,依次取出action链表中与trigger匹配的action。 每次均执行一个action中的一个command对应函数(一个action可能携带多个command)。 当一个action所有的command均执行完毕后,再执行下一个action。 当一个trigger对应的action均执行完毕后,再执行下一个trigger对应action。
    */
    while (true) {
        if (!waiting_for_exec) {
            //执行该command对应的处理函数
            am.ExecuteOneCommand();
            //restart_processes()去重启在service_list列表中带有所有带有SVC_RESTARTING标志(该标志是在wait_for_one_process()处理中设置的),最终会调用service_start()函数去重新启动一个退出的服务
            restart_processes();
        }

        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

        if (am.HasMoreCommands()) {
            timeout = 0;
        }

       bootchart_sample(&timeout);

        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
   }
    return 0;
}

zygote

通过查看init进程的源码,我们对init的初始化过程有了一个大致的了解,这个时候,zygote这个service 已经被启动了,我们先看一下zygote的配置文件,伴随zygote一起启动的还有audioserver,cameraserver,media等底层server

service zygote /system/bin/app_process -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

现在我们可以来看看zygote了,它的源码位于/frameworks/base/cmds/app_process/app_main.cpp,这个文件的main方法大致如下

int main(int argc, char* const argv[]){
    ……
    //AppRuntime继承自AndroidRuntime
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ……
    //如果init.rc配置中有--Zygote,参数为com.android.internal.os.ZygoteInit
    if (zygote) {
      runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
   } else if (className) {
       runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
   } else {
       fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
   }
}

可以看到,这个main方法主要做的就两件事

  1. 创建AppRuntim对象
  2. 调用AppRuntim的start方法

所以接下来我们接着分析AppRuntime的源码,在分析AppRuntime前,先大致了解一下zygote的启动时序流程,如下图

Android底层启动解析_第2张图片

现在再来看看AppRuntime,也就是AndroidRuntime的源码,它的源码位于/frameworks/base/core/jni/AndroidRuntime.cpp,我们来看看它的start方法

void AndroidRuntime::start(const char* className, const Vector& options, bool zygote){
    ……
    //1调用startVm方法来启动虚拟机 
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    //2调用startReg来注册Android JNI函数
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
     return;
    }
    ……
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);

    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
    //3调用CallStaticVoidMethod方法来调用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 */
      } else {
          //通过JNI调用Java函数,进入Java世界
           env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
    }
……
}

启动虚拟机和注册Android JNI函数的详细过程之后在分析,这里我们只需要知道大致过程便可,由于这儿已经注册了JNI函数,之后的java代码只需要通过JNI的方法就可以调用这些Native方法,在第三步中,它会根据输入的参数com.android.internal.os.ZygoteInit和RuntimeInit,通过反射以及JNI,找到相应的类,并调用类的main方法,ZygoteInit类在这个时候变调用,这个时候便进入了java层。

zygoteInit

接下来在看看ZygoteInit.java这个类,它的源码位于/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
这个类主要做的事情有以下几件
①调用registerZygoteSocket函数创建了一个socket接口,用来和ActivityManagerService通讯
②调用startSystemServer函数来启动SystemServer组件

  • 调用startSystemServer创建system_server进程。
  • SystemServer调用handleSystemServerProcess完成自己的使命。
  • handleSystemServerProcess抛出异常,最终调用com.android.server.SystemServer的main函数。
  • main函数加载libandroid_server.so并调用native的init1函数。
  • init1函数通过JNI调用com.android.server.SystemServer类的init2函数。init2函数创建一个线程,用于加载各种service。
  • init1函数最终加入到Binder通信系统。

③调用runSelectLoopMode函数进入一个无限循环在前面创建的socket接口上等待ActivityManagerService请求创建新的应用程序进程,其过程如下

  • SystemServer进程发送消息到Zygote
  • Zygote通过fork创建子进程
  • 子进程调用android.app.ActivityThread的main函数

我们来看看main方法里面的源码

public static void main(String argv[]) {
    //Android7.0在zygoteInit中对线程的使用做了限制,不让在zygote中新建线程
    //主要是由于担心用户新建线程提高预加载速度 ,但是可能没做好同步工作, 当有的应用需要预加载的资源,但是多线程情况下还没有加载,发生问题  
    ZygoteHooks.startZygoteNoThreadCreation();
    try {
           ……
            // 1、注册监听
            registerZygoteSocket(socketName);
            // 2、预加载一些关键的类
            preload();
            ……
            // 3、启动system_server进程
            if (startSystemServer) {
                startSystemServer(abiList, socketName);
            }

            //4监听其他进程发出来的请求
            runSelectLoop(abiList);

            //关闭socket
            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (Throwable ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

至此Android的启动流程也就分析完成了,我们回过头再来看看Android的启动流程图,心中对各种流程的执行顺序也就清晰了
Android底层启动解析_第3张图片

你可能感兴趣的:(Android源码解析)