这一期我们来看一下我们
init
进程是如何执行这些脚本,和创建服务、守护服务的。
首先来看一下
init
进程在后期所做的一些工作
当我们吧脚本解析完之后,就会把我们的解析结果放到两个结构中,一个是action_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
的死循环,在这里边就是执行我们的每一条命令,并且去守护我们的服务。