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

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


这是一张官方给出的系统架构图,通过上图我们可以知道,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的启动时序流程,如下图

现在再来看看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的启动流程图,心中对各种流程的执行顺序也就清晰了

更多相关文章

  1. Android(Java):android横竖屏切换
  2. 有关Android线程的学习
  3. android之调用webservice 实现图片上传下载
  4. Android(安卓)Framework 分析---3PackageManager 分析
  5. 有关Android线程的学习
  6. android之调用webservice 实现图片上传
  7. Android面试系列文章2018之Android部分之自定义View篇
  8. Android的多任务之路
  9. Android与H5互调详细介绍

随机推荐

  1. Android 模拟滑动 MotionEvent touch事件
  2. Android网络图片加载框架的选择
  3. Android(安卓)Notification 的使用
  4. Android之事件处理全面剖析
  5. Android《第三章:AIDL与Messenger》
  6. Android 系统(125)---Android通过Dialer实
  7. 关于Android横竖屏切换的解决方法
  8. Textview的maxLines大于1行时,"android:el
  9. android XML解析详解(封装好的工具类)
  10. Android NDK: Host 'awk' tool is outdat