本文从Android系统的源代码下载开始介绍,紧接着介绍了如何将Android系统源代码编译成Android系统镜像文件,然后对Android系统的启动流程进行了较为深入的讲解
Android源码下载
Android系统的编译环境目前只支持 Linux 以及 Mac OS 两种操作系统。如果采用虚拟机安装时需要考虑占用磁盘空间(源码+编译) ,如果是2.3 源码需要5G,编译需要10G。google推荐使用64位的操作系统。源码下载编译环境初始化官网地址,很可惜,目前该网址是访问不了的,已经被屏蔽了。想要访问可以采取的方法。
Android源码编译
Android源码查看
首先给大家介绍一个查看源码工具:SourceInsight
通过上面的软件我们就可以查看任意我们想看的源码了,其实如果我们的eclipse关联了sdk源码,那么看起来比这个软件方便多了,那么为什么还用这个软件呢,因为该软件可以看整个Android系统的任意文件,这一点是比较强大的。
那么下面就让我带大家一起查看一下Android系统启动流程吧。
Android启动流程
Init进程的启动
init进程,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。init始终是第一个进程。启动过程就是代码init.c中main函数执行过程:system\core\init\init.c
int main(int argc, char **argv)
{
/* Get the basic filesystem setup we need put
* together in the initramdisk on / and then we'll
* let the rc file figure out the rest.
*/
mkdir("/dev", 0755);
mkdir("/proc", 0755);
mkdir("/sys", 0755);
mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);
/* We must have some place other than / to create the
* device nodes for kmsg and null, otherwise we won't
* be able to remount / read-only later on.
* Now that tmpfs is mounted on /dev, we can actually
* talk to the outside world.
*/
open_devnull_stdio();
log_init();
INFO("reading config file\n");
init_parse_config_file("/init.rc");
/* pull the kernel commandline and ramdisk properties file in */
import_kernel_cmdline(0);
get_hardware_name(hardware, &revision);
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
init_parse_config_file(tmp);
action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(property_init_action, "property_init");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
queue_builtin_action(set_init_properties_action, "set_init_properties");
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail);
action_for_each_trigger("early-fs", action_add_queue_tail);
action_for_each_trigger("fs", action_add_queue_tail);
action_for_each_trigger("post-fs", action_add_queue_tail);
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
queue_builtin_action(check_startup_action, "check_startup");
/* execute all the boot actions to get us started */
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
/* run all property triggers based on current state of the properties */
queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers");
#if BOOTCHART
queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
for(;;) {
int nr, i, timeout = -1;
execute_one_command();
restart_processes();
上面源代码已经省略去了非重要部分。
通过上面的代码我们可以看到在main函数中执行了:文件夹建立,挂载,rc文件解析,属性设置,启动服务,执行动作,socket监听等一系列操作。
下面看两个重要的过程:rc文件解析和服务启动。
1.1 rc文件的解析
.rc文件是Android使用的初始化脚本文件,系统初始化要触发的动作和要启动的服务及其各自属性都在rc脚本文件中定义。 具体看一下启动脚本:\system\core\rootdir\init.rc
该脚本定义的内容如下:
# setup the global environment
# create mountpoints
# Create cgroup mount point for cpu accounting
# Filesystem image public mount points.
# Create cgroup mount points for process groups
# create basic filesystem structure
## Daemon processes to be run by init
service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server
在解析rc脚本文件时,将相应的类型放入各自的List中:
\system\core\init\Init_parser.c:init_parse_config_file( )存入到action_queue、action_list、service_list中,解析过程可以看一下parse_config函数。
这其中包含了服务:adbd、servicemanager、vold、ril-daemon、debuggerd、surfaceflinger、zygote、media……
1.2 服务启动
文件解析完成之后将service放入到service_list中。在main函数中,调用restart_process();
在restart_processes()方法中调用了restart_service_if_needed。
在restart_service_if_needed()中调用了service_start()。
在service_start中开启了子进程去运行各个服务。
Init.rc中第一个Service是servicemanager
ServiceManager用来管理系统中所有的binder service,不管是本地的c++实现的还是java语言实现的都需要这个进程来统一管理,最主要的管理就是,注册添加服务,获取服务。所有的Service使用前都必须先在servicemanager中进行注册。
代码位置:frameworks\base\cmds\servicemanager\Service_manager.c
系统开启了很多个服务,其中最重要的就是zygote服务。该服务是在Init.rc中定义的。
2、Zygote进程的启动
Zygote这个进程是非常重要的一个进程,Zygote进程的建立是真正的Android运行空间,初始化建立的Service都是Navtive service。
代码位置:frameworks/base/cmds/app_process/App_main.cpp
上面是App_main.cpp的代码片段,最重要的就是通过AppRuntime开启了com.andrioid.internal.os.ZygoteInit程序。这是一个质的飞跃,让我们从c/c++的世界跑到了Java世界。
上面调用了AndroidRuntime类中的start方法。
AndroidRuntime.cpp代码位置:frameworks\base\core\jni\AndroidRuntime.cpp
我们需要关注的就是其中的start方法。
3、Java界的第一个类ZygoteInit
代码地址:frameworks\base\core\java\com\android\internal\os\ZygoteInit.java
该类是Java的入口函数,包含了Java的main函数。
在ZygoteInit中完成了Android核心类和资源的加载,并启动了Android的系统服务。
3.1 preloadClasses();
在preloadClasses()方法中加载了1.8k多个Android类。这些类是定义在一个名为preloaded-classes的文件中。
3.2 startSystemServer();
startSystemServer()正如其名称,开启了系统服务进程。开启服务进程是通过参数调用Linux命令行进行的。这些命令参数作为开启服务的参数事先被设置。开启系统服务调用的是Zygote.forkSystemServer方法,该方法如下:
可以看到上面的方法是一个native方法,这里Java又调回到C语言的内容了,具体是怎么开启的Android系统服务暂且不去追究。我们继续往下走。
如果开启子进程成功的话 ,那么返回的pid==0,这时候调用了handleSystemServerProcess(parsedArgs);
方法。我们有必要看看该方法都干了啥。
上面的代码完成了系统服务进程的剩余工作。剩余工作是在RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
方法中完成。
这里面完成的工作包括:设置输如输出流为AndroidPrintStream、设置默认未捕获异常处理handler、设置日志管理器、设置user-Agent、设置VM Trace参数,给系统服务进程设置nice-name ,起个好听的名字也就是。
最后通过抛出一个异常,在捕获异常的代码通过反射的方法调用SystemServer的main方法,将SystemServer进程给运行起来,这里就是一个非常有意思的地方,Android系统服务进程是用通过抛出异常然后捕获异常,然后处理捕获的异常的时候运行起来的。为什么这么做呢?我从代码中找到了如下的说明,这样是唯一能找到的理由!
我翻译一下吧,大概意思是这样子的:
这里抛出的异常将在ZygoteInit.main()方法中捕获到,这个main方法的职责是调用异常的run方法(通过看代码我们可以发现这个异常是自定义的额包装为Thread的异常,这也是设计者的高明之处)。这样的安排清除了所有的栈队列中的帧(frames,翻译成帧并不能很好的表达Java中的思想),这些帧是用来设置进程用的(进程设置好了,你就通过抛异常的方法给人家干掉了,有点卸磨杀驴的感觉)。
补充知识:Stack frame(堆栈帧)是一个为函数保留的区域,用来存储关于参数、局部变量和返回地址的信息。堆栈帧通常是在新的函数调用的时候创建,并在函数返回的时候销毁。通常main是栈中的第一个帧,一个方法的调用就会创建一个帧。当遇到return或遇到Exception时帧会被销毁。
4、SystemServer进程
在上一节中我们知道ZygoteInit进程启动了SystemServer进程,虽然是在异常中启动的,SystemServer就像一个私生子就这么诞生了。那么这一节我们重点分析一下这个私生子生活的怎么样。
源码位置:frameworks\base\services\java\com\android\server\SystemServer.java
SystemServer作为一个独立的进程(也是一个独立的Java程序),入口函数当然也是main。那么显然分析该类必须从main函数开始。
在main函数中做了以下工作:
1)如果系统时间早于1970年,则把时间设置为1970年。
2)如果性能统计开关已经打开,则开始统计性能,同时开启周期任务每一个小时打印一次日志。
3)设置目标堆内容使用率上限为0.8倍
4)加载C/C++库
5)调用init1()方法进行初始化
4.1 init1()
该方法的注释意思是:该方法被Zygote方法调用用于初始化系统。这将导致本地服务(SurfaceFlinger、AudioFlinger)的运行。最后该方法又调用了init2()方法,用于启动Android服务。
4.2 init2()
在init2()方法中开启了一个android.server.ServerThread线程。那么就看看该线程都干了些什么吧?
ServerThread的run()方法太长了,不可能把所有的代码都粘贴出来,这里就截取一部分,略窥一斑吧。
大家看到了上面的代码有没有惊呆呢,原来我们经常用的各种Service都是这里启动的,这次终于找到服务的源头了吧。这些服务都通过 ServiceManager.addService()添加到了ServiceManager中。这样得以让我们在Activity中可以拿到这些Service。
代码追溯到这里我们的桌面也该启动了吧,因为ActivityManagerService已经启动。那么接下来我们就看看((ActivityManagerService)ActivityManagerNative.getDefault()) .systemReady()都干了什么事情吧。
5、HomeActivity的启动
上面代码执行很长一段逻辑,很难看的懂,但是最后一行调用了resumeTopActivityLocked方法。我们看看这个方法里面都干了些什么事情。
上面代码高亮部分执行了HomeActivity。至此我们的桌面系统就开始加载。
6、启动流程图
上面的启动流程是从Linux的内核启动好开始启动Init进程开始,到启动好桌面为止的,相信绝大多数人看了上面的整个流程已经晕掉了。为了帮助大家简单的记住Android系统大致的启动流程,我从网上找了如下一张图片,供大家参考。