本文来自于CSDN博客,作者:天堂1223,已获授权,版权归原作者所有,未经作者同意,请勿转载。
欢迎同有博客好文章的作者加微信(ID:tm_forever_miss)或直接邮件(mobilehub@csdn.net)投稿、约稿、给文章纠错。
在这篇文章中,我们将从代码角度仔细学习Android系统的启动过程,同时,学习Android启动过程中的初始化脚本语言,即init.rc中的语言语法。在这里,不在详细介绍Linux内核的启动过程,主要学习从Linux内核启动之后,init初始化是如何工作的,他是如何启动Android系统的第一个进程–Zygote进程。并且还会继续了解后面其他的进程是如何通过Zygote进程启动的。话不多说,我们现在就来学习Android系统启动之路。
Android系统启动流程图
我们都知道,Android系统内核是基于Linux内核,所以在Android系统启动过程中,首先启动Linux内核,Bootloader加载并启动Linux内核,内核启动完成之后,内核开始启动Android系统的init进程,然后init进程通过init.rc启动脚本语言的执行,来启动Zygote进程,作为Android其他进程的父进程,Zygote进程做完初始化工作之后,启动SystemServer来启动其他系统服务。
下面我们从init进程的启动开始学习。
该文件位于system/core/init/init.cpp中,我们来看看init进程都做了哪些工作。
首先,init进程添加环境变量,并且挂载相应的目录。在主目录/之外为kmsg和null创建设备节点。初始化selinux,由于我们这里并不研究selinux的运行机制,所以其初始化细节也不在详究。根据起注释可以知道,如果当前系统处于内核域中,重新执行init来转换到init域中,因为SELinux策略已经被加载了。下面接着通过restorecon命令来将在selinux启动之前创建的目录的安全上下文恢复到正确的属性。
接着,便是信号处理机制的初始化工作,加载启动属性,并启动属性服务器。下面,便进入至关重要的一个函数,也是init进程的主要工作,便是执行init_parse_config_file("/init.rc")函数,该函数的主要作用就是解析init.rc文件,并执行init初始化进程语言。下面我们来看一下这个函数:
该代码位于system/core/init/init_parser.cpp中,该函数读取init.rc文件,并将数据传入到parse_config(path, data)函数中。
我们来看一下parse_config函数:
该函数和刚刚那个函数位于同一个文件中,很明显,该函数用于解析读取的init.rc文件的字符串,该函数与文件parse.cpp中的next_token()函数配合,进行字符串的解析,然后通过调用parse_new_section()函数将services和actions等添加到运行队列中,等待trigger触发器的触发运行。
有关与Android init language(Android初始化语言)我在博客 Android Init Language(android初始化语言)中已经进行了详细的介绍。下面我们接着看init进程中的main函数中所做的工作:
init.rc解析完成之后,所有的启动项目都被放入到action_add_queue_tail中,接着调用action_for_each_trigger("early-init", action_add_queue_tail),触发early-init触发器来出发这些相关services和actions的运行。
我们来看一下,在init.rc中,我们看early-init相关启动的services和actions。在这里基本上是恢复某些文件或文件夹的安全上下文,然后调用init_zygote32_64.rc文件中的命令启动zygote。
这里的意思是通过/system/bin/app_process32程序启动zygote进程,参数为--zygote和--start-system-server,这两个参数在后面我们会用到的。下面我们来看一下Zygote的main函数。
该代码位于frameworks/base/core/Java/com/android/internal/os/ZygiteInit.java中。在代码中,他首先通过参数判断是否启动systemServer,也就是通过刚刚我们记录下来的参数--start-system-server判定startSystemServer为true;接着通过调用registerZygoteSocket(socketName)函数来注册zygote套接字,进行进程间的通信,我们来看一下这个函数:
该代码位于同一个文件中,通过代码我们可以看到,创建zygote套接字的方式是通过创建一个LocalServerSocket对象来建立进程间的通信,在注释中有说明,注册一个服务套接字用于zygote命令连接。
创建完成zygote套接字之后,执行preload()函数来进行资源文件的预加载工作。
在这里,加载类,加载资源文件,加载OPenGl,加载共享库,文本资源以及准备WebView。这里不在所说,我们接着往下看。
接着便调用startSystemServer(abiList, socketName)方法启动系统服务,我们来看一下这个函数:
在这里准备系统服启动的参数,并通过forkSystemServer来创建系统服务进程,接着调用handleSystemServerProcess(parsedArgs)来进行系统服务的处理。
在这里完成对创建的系统服务剩余的工作,最后调用RuntimeInit.zygoteInit将剩余的参数传递到系统服务中。
该函数位于同目录下面的RuntimeInit.java文件中。这里的nativeZygoteInit()用于进程间通信的初始化操作,applicationInit函数用于服务的启动,我们来看一下:
该函数主要是一些运行时的一些工作,同时调用invokeStaticMain来进行main函数的执行,看这个方法的注释,意思便是剩下的参数被传递,用于启动类的静态main函数。这里传递的类参数便是SystemServer类,所以这个函数的工作便是启动SystemServer的main函数。
该函数位于frameworks/base/services/java/com/android/server/SystemServer.java类中,这里运行SystemServer的run函数,该函数设置环境变量,启动其他服务,后面就不再赘述了。
接着返回到ZygoteInit.java的main函数中,这个函数最后执行runSelectLoop函数,这里运行一个循环,接收新的连接,读取来自连接的命令。
至此,Android系统的启动过程就到了启动系统服务以及其他服务阶段了,后面我们会在别的博客中进行学习和讲解。
了解最新移动开发、VR/AR 干货技术分享,请关注 mobilehub 微信公众号(ID: mobilehub)。
标签: javasocket客户端端口号