这一期呢,我们主要是学习我们的
init
进程脚本如何解析我们的启动脚本的,我们
init
进程做完一些初始化的工作之后就会解析我们的启动脚本,他的启动过程主要分为下面几个部分

第一个就是使用init_parse_config_file
函数来解析我们的启动脚本,把我们的启动脚本的每一个命令全部解析到我们的一个列表中;
第二个就会调用
action_for_each_trigger
和
queue_builtin_action
这两个函数把我们所解析的相关的操作加到我们的
action_queue
的队列中,然后就会从
action
队列中取相应的操作创建相关的服务或执行相关的命令,最后再去守护我们相关的服务
那么我们首先来看一下我们解析脚本的一个过程

首先我们会调用init_parse_config_file
来解析我们的
init.rc
,在解析前,我们首先打开我们的
init.rc
,然后调用
parse_config
这个函数来解析我们的启动脚本,在解析过程中主要涉及到以下几个数据结构和链表
我们的数据结构包括:
import_list
和
struct listnode
import_list
主要是用来保存我们
init.rc
中所导入的其他脚本文件,比如
init.usb.rc
等
struct listnode
主要是用来保存启动脚本中所有的操作命令
而我们的三个链表呢,是用来保存我们命令的这三个链表分别是:
service_list
、
action_list
、
action_queue
service_list
主要是用来保存我们脚本中所有的服务
action_list
主要是用来保存我们所有的操作
action_queue
主要是执行的链表
我们解析完的服务和相关的操作全都放在
service_list
和
action_list
这两个链表中,然后我们会使用其他的方法,把我们的在初始化过程中需要执行的服务和命令添加到我们的
action_queue
链表中,最后由我们的
init
进程根据
action_queue
中服务和命令来执行相关的操作,我们代开代码来看一下
init_parse_config_file
函数

他首先会打开我们这个文件,然后调用parse_config
这个函数来解析这个文件,这个解析文件就是一个简单的语法解析器

在这里会有一个parse_state
变量,我们后边在看,我们再看他解析过程中,首先就是调用我们的
next_token
,
next_token
就是一个语句中的最小单元,我们再看一下我们的启动脚本

我们一个最小的单元就是指:on
、
early-init
、
write
、、
proc/1/oom_adj
、
-16
他们都是独立的单元,下面我们来看一下我们的
next_token
,

他一共有三个返回值,第一个是T_EOF
,也就是我们的文件解析完成,完成之后就走到了我们的
parser_done

他在这里就会把我们的import_list
列表中所包含的其他的
init
脚本,在这里重新做解析,这就是在解析完之后他会导入其他的脚本
那我们的第二个返回值是
T_NEWLINE
,他在执行的过程中会首先看一下我们的关键字,也就是我们返回的第一个
args
,如果说第一个
args
是
SECTION
的话,那么他就会调用
state.parse_line
做一个解析,然后调用
parse_new_section
这个函数,稍后我们会看到这两个函数的意思
我们再来看一下第三个返回值,他是
T_TEXT
,他在这里就相当于我们命令中的一个参数。
下面我们来看一下我们
next_token
的一个具体实现

他的解析过程是根据我们这里的变量
parse_state
来决定的,这里的
parse_state
有一个
nexttoken
,这个
nexttoken
保存着我们解析的一个状态,如果说我们在上一次解析中把这个
nexttoken
给设置了,那么我们会首先执行上次所设置的状态,在这个状态中会直接返回,然后把这个状态直接清
0
,交给我们的
parse_config
,而我们没有设置这个状态呢,我们就会真正的去解析这个脚本

他解析的过程呢是一个字符一个字符的去判断,如果我们遇到的是
0
,那么我们就认为这个文件解析完成,返回一个
T_EOF
,如果我们遇到一个“
\n
”,那么就说明我们遇到了一个新的一行,这时就会返回一个
T_NEWLINE
,如果我们遇到其他字符可以继续操作,如果说我们遇到一个
#
号,这个是一个注释行,我们可以把它忽略,然后返回一个新的行。
其实从第一个
for
循环我们可以看到,我们这个
for
循环的作用就是把我们脚本中没用的字符,比如说
\n
、
\t
、以及无用的空格等给清除掉,过滤掉,因为我们调用一次肯定是上次遇到了一个完整的单词,或者说遇到了一个完整的行,进来之后呢,他里边第一个必须遇到一个正常的字母,然后才能说明他有一个新的
token
,这是我们第一个
for
循环的作用。如果我们在
for
循环最后遇到一个字符,就会调到我们的
text
标签中执行,下面我们来看一下我们的
text

在这里主要是解析我们最小的一个token
,如果说我们上一个遇到了一个字符,那么他在这里就会解析这个单词的结束,如果下一个是
0
的话,他在这里就会走一下
textdone
,如果我们遇到的是空格、
\n
、
\t
、
\r
我们就说我们遇到了一个完整的单词 ,这个单词就会返回,如果遇到
\n
,我们可以看到我们在这里设置了
nexttoken
,是
T_NEWLINE
,然后在这里也走到了
textdone
,如果我们遇到了一个引号,那么它就会去做一些相关的事情,接下来来我们来看一下我们的
textdone

在这里textdone
他就是标记了这个字符的一个级数位置,然后返回了一个
T_TEXT
,也就是返回了一个完整的单词,这就是我开门
nexttoken
所做的一个操作,了解了
nexttoken
之后呢,我们就知道了解析的一个最小的元素是如何来生成的,然后我们会根据返回值做一些不同的操作,如果说我们返回的是一个
T_NEWLINE
,那么我们就可以查找一下我们下一行是一个什么样的语法,下面我们来看一下
lookup_keyword
的实现,我们将下面的内容放到下一期来学习。