过去的这一年多里,做过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++文件,该代码所做的主要有六件事情
- 挂载了tmpfs,devpts,proc,sysfs这4类文件系统
- 屏蔽标准的输入输出,并且初始化android自己的log系统
- 初始化Android系统所需的属性值
- 创建SELINUX(一种安全系统)
- epoll一个套接字fd,当init初始化完成后,变成一个守护进程,这个fd便可以循环监听事件
- 初始化SIGCHLD信号处理
- 解析init.rc,初始化zygote、audioserver、cameraserver、media、netd、wificond,并将解析的数据通过socket传送,后面的循环中会处理解析出来的action
- 当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, "mod