这一期我们来看一下我们init进程是如何执行这些脚本,和创建服务、守护服务的。 首先来看一下init进程在后期所做的一些工作
当我们吧脚本解析完之后,就会把我们的解析结果放到两个结构中,一个是ac tion_list ,一个是service_list,在解析完之后我们会使用我们的action_for_each_trigger来将解析脚本中的相关操作添加到我们的action队列中,添加完之后他会在后面创建服务和执行命令的时候,来对我们的action_queue进行操作,我们这里也提供了一个我们自己构建命令的一个函数(quene_builtin_action),他是在我们代码中直接创建命令,并且添加到我们的action_queue和action_list中,当把所有的启动命令构建完成之后,就开始一条一条的执行我们的启动脚本创建并且守护我们相关的服务。那么下面我们来看一下我们解析完我们的启动脚本之后,我们的service_list和action_list这两个队列的结构
我们的service_list和action_list其实就是一个双向链表,当我们解析完一个命令或者一个服务之后,他就会把我们的服务或者命令插入到我们的service_list和action_list中,我们的service_list用来保存我们解析过程中的service,而我们的action_list就是把我们的相关操作命令给添加进来。 我们先来看一下service_list,他的节点全是我们init脚本中的一些服务,节点内容就是我们的服务的二进制文件以及执行的参数,在后面我们回家一下相关的参数,比如说他的user和group等,都会在这里添加出来,所以说他的结构就是这样的,一个service连接这下一个service,一直到最后一个service,在每个service后面都会有一个相关的属性,这就是我们service的一个数据结构。 下面我们来看一下我们的action_list,他的节点都是我们on打头的一个section,在这个section下会有n个执行命令,比如我们的mkdir、write等,一个on加我们的early-init或者init、post-fs等他下面都会挂接几个命令,而这几个命令,他也是一个双向链表,添加在我们这个on的节点下面,而我们的on init下面也会有一个cmd_list,这就是我们解析完之后action_list的一个数据结构。 下面我们来看一下,我们init进程是如何处理这两个数据结构的,下面我们来看一下我们整个事件的列表
这个处理函数是由我们的action_for_each_trigger决定的,他在这里面会有一个事件的名字,和一个执行的函数(on early-init,func),他的大概过程是这样的,首先要遍历我们的action_list,找到我们的section,比如我们现在的section是on early-init,那么他首先要找到我们的section,这个section就是我们node的名字,当他和我们的on early-init一致的话,我们就是用func去执行我们的命令,这个func所做的事情就是将我们section下面所对应的cmd_list添加到我们的action_queue中,这样就完成了一次添加,如果我们后面要添加on boot的话,我们就去找on boot的cmd_list,然后再将他的cmd_list添加到我们的cmd_list中,依次是cmd_list1、cmd_list2......这样的话我们所有的操作都会添加到我们的action_queue中。 当我们添加完成之后,我们还可以添加一些自己的命令,比如在程序代码中写一些命令,那么我们可以使用queue_builtin_action这个函数,这个函数就是创建一个action node,并且将action node添加到我们的action_list和我们的action_queue中,当我们完成这一系列的添加之后,我们就可以交给我们的init进程来执行action_queue中的所有操作。 接下来我们来看一下action_for_each_trigger这个函数,我们打开我们的init.c文件,我们来看一下在我们init.rc解析完之后是如何处理的,首先我们看一下action_for_each_trigger这个函数
他呢就是根据我们的trigger来找到我们的section,并且如果说我们的trigger和我们的section的名字是相同的,那么我们就是用func对我们的节点进行操作 下面我们再来看一下我们的queue_builtin_action
这个函数的功能就是创建一个自己的action,并且把它添加到我们的action_queue和action_list中,首先创建一个action的节点,将我们的函数指针以及名字赋值到我们的结构中,然后把它添加到action_list和action_queue中,这就是构建一个自己的操作。 当我们的整个操作创建完之后,我们就会在我们init后面的执行过程中去一条一条的执行我们的操作。 下面我们打开我们的init.rc,看一下我们的service的定义
Service会有一个名字,后面对应我们的执行程序,也有可能我们后面还会加一些启动参数, 在这个service下面还会有一些属性,属性包括class等,class还会分为class core和class main 下面我们来看一下我们这个服务的类型,以及我们如何来一条一条的执行我们所有的操作 我们来看一下service的分类
Service主要分为三类,class core 、class main 、class default 我们再来看init进程是如何来执行我们的命令的
首先我们的init进程会调用我们的execute_one_command,去我们的action_queue队列中取一条命令,我们的一个命令其实对应的是keyword,我们的keyword是一个名字加属性,加我们的执行函数,加我们的执行参数,而我们的keyword又分好多种,根据我们的func分类,他可以分为do_chdir 、do_class_start 、do_rmdir等,当我们每取出一个命令之后,就会去做相应的操作,去创建目录,建立链接,设置环境变量等一系列操作,还有一个就是do_class_start操作,他后面会跟一个参数,这个在init.rc中是有体现的,当我们去执行他的时候,我们后面会跟一个服务类的名称,这个名称就会去调用我们的service_for_each_class,来对我们的service_list进行遍历,如果do_class_start后面跟的类名,和我们的服务某一个节点的名字相同的话,我们就会调用我们的service_start,去启动我们相关的服务,这就是我们服务启动的一个过程。 下面我们来看一下我们代码的具体实现
当我们构建完所有的action_queue之后呢,我们进入了一个for的死循环,在这里边就是执行我们的每一条命令,并且去守护我们的服务。
|