init进程【2】——解析配置文件

【转账自这里】http://blog.csdn.net/zhgxhuaa


在前面的一篇文章中分析了init进程的启动过程和main函数,本文将着重对配置文件(init.rc)的解析做一下分析。

init.rc脚本语法

init.rc文件不同于init进程,init进程仅当编译完Android后才会生成,而init.rc文件存在于Android平台源代码中。init.rc在源代码中的位置为:@system/core/rootdir/init.rc。init.rc文件的大致结构如下图所示:



关于init.rc脚本的介绍,在@system/core/init/readme.txt中有完整的介绍,这里不再赘述,不想看英文的朋友也可以看下面的部分,这个部分关于rc脚本的介绍转自http://blog.csdn.net/nokiaguy/article/details/9109491。相当于readme的翻译吧。

init.rc文件并不是普通的配置文件,而是由一种被称为“Android初始化语言”(Android Init Language,这里简称为AIL)的脚本写成的文件。在了解init如何解析init.rc文件之前,先了解AIL非常必要,否则机械地分析init.c及其相关文件的源代码毫无意义。

为了学习AIL,读者可以到自己Android手机的根目录寻找init.rc文件,最好下载到本地以便查看,如果有编译好的Android源代码,在<Android源代码根目录>out/target/product/geneic/root目录也可找到init.rc文件。

AIL由如下4部分组成。

1.  动作(Actions)

2.  命令(Commands)

3.服务(Services)

4.  选项(Options)

     这4部分都是面向行的代码,也就是说用回车换行符作为每一条语句的分隔符。而每一行的代码由多个符号(Tokens)表示。可以使用反斜杠转义符在Token中插入空格。双引号可以将多个由空格分隔的Tokens合成一个Tokens。如果一行写不下,可以在行尾加上反斜杠,来连接下一行。也就是说,可以用反斜杠将多行代码连接成一行代码。

     AIL的注释与很多Shell脚本一行,以#开头。

     AIL在编写时需要分成多个部分(Section),而每一部分的开头需要指定Actions或Services。也就是说,每一个Actions或Services确定一个Section。而所有的Commands和Options只能属于最近定义的Section。如果Commands和Options在第一个Section之前被定义,它们将被忽略。

Actions和Services的名称必须唯一。如果有两个或多个Action或Service拥有同样的名称,那么init在执行它们时将抛出错误,并忽略这些Action和Service。

下面来看看Actions、Services、Commands和Options分别应如何设置。

Actions的语法格式如下:

[plain]  view plain copy
  1. on <trigger>  
  2.    <command>  
  3.    <command>  
  4.    <command>  

也就是说Actions是以关键字on开头的,然后跟一个触发器,接下来是若干命令。例如,下面就是一个标准的Action

[plain]  view plain copy
  1. on boot  
  2.     ifup lo  
  3.     hostname localhost  
  4.     domainname localdomain  

其中boot是触发器,下面三行是command

那么init.rc到底支持哪些触发器呢?目前init.rc支持如下5类触发器。

1.  boot

   这是init执行后第一个被触发Trigger,也就是在 /init.rc被装载之后执行该Trigger 

2.  <name>=<value>

   当属性<name>被设置成<value>时被触发。例如,

on property:vold.decrypt=trigger_reset_main

    class_reset main

3.  device-added-<path>

    当设备节点被添加时触发

4.  device-removed-<path>

   当设备节点被移除时添加

5. service-exited-<name>

   会在一个特定的服务退出时触发

Actions后需要跟若干个命令,这些命令如下:

1.  exec <path> [<argument> ]*

  创建和执行一个程序(<path>)。在程序完全执行前,init将会阻塞。由于它不是内置命令,应尽量避免使用exec ,它可能会引起init执行超时。

    2.  export <name> <value>

在全局环境中将 <name>变量的值设为<value>。(这将会被所有在这命令之后运行的进程所继承)

3.  ifup <interface>

   启动网络接口

4.  import <filename>

   指定要解析的其他配置文件。常被用于当前配置文件的扩展

5.  hostname <name>

   设置主机名

6.  chdir <directory>

   改变工作目录

7.  chmod <octal-mode><path>

   改变文件的访问权限

8.  chown <owner><group> <path>

   更改文件的所有者和组

9.  chroot <directory>

  改变处理根目录

10.  class_start<serviceclass>

   启动所有指定服务类下的未运行服务。

11  class_stop<serviceclass>

  停止指定服务类下的所有已运行的服务。

12.  domainname <name>

   设置域名

13.  insmod <path>

   加载<path>指定的驱动模块

14.  mkdir <path> [mode][owner] [group]

   创建一个目录<path> ,可以选择性地指定mode、owner以及group。如果没有指定,默认的权限为755,并属于root用户和 root组。

15. mount <type> <device> <dir> [<mountoption> ]*

   试图在目录<dir>挂载指定的设备。<device> 可以是mtd@name的形式指定一个mtd块设备。<mountoption>包括 "ro"、"rw"、"re

16.  setkey

   保留,暂时未用

17.  setprop <name><value>

   将系统属性<name>的值设为<value>。

18. setrlimit <resource> <cur> <max>

   设置<resource>的rlimit (资源限制)

19.  start <service>

   启动指定服务(如果此服务还未运行)。

20.stop<service>

   停止指定服务(如果此服务在运行中)。

21. symlink <target> <path>

   创建一个指向<path>的软连接<target>。

22. sysclktz <mins_west_of_gmt>

   设置系统时钟基准(0代表时钟滴答以格林威治平均时(GMT)为准)

23.  trigger <event>

  触发一个事件。用于Action排队

24.  wait <path> [<timeout> ]

等待一个文件是否存在,当文件存在时立即返回,或到<timeout>指定的超时时间后返回,如果不指定<timeout>,默认超时时间是5秒。

25. write <path> <string> [ <string> ]*

向<path>指定的文件写入一个或多个字符串。  

Services (服务)是一个程序,他在初始化时启动,并在退出时重启(可选)。Services (服务)的形式如下:

[plain]  view plain copy
  1. service <name> <pathname> [ <argument> ]*  
  2.       <option>  
  3.       <option>  

例如,下面是一个标准的Service用法

[plain]  view plain copy
  1. service servicemanager /system/bin/servicemanager  
  2.     class core  
  3.     user system  
  4.     group system  
  5.     critical  
  6.     onrestart restart zygote  
  7.     onrestart restart media  
  8.     onrestart restart surfaceflinger  
  9.     onrestart restart drm  
Services的选项是服务的修饰符,可以影响服务如何以及怎样运行。服务支持的选项如下:

1.  critical

表明这是一个非常重要的服务。如果该服务4分钟内退出大于4次,系统将会重启并进入 Recovery (恢复)模式。

2. disabled

表明这个服务不会同与他同trigger (触发器)下的服务自动启动。该服务必须被明确的按名启动。

3.  setenv <name><value>

在进程启动时将环境变量<name>设置为<value>。

4.  socket <name><type> <perm> [ <user> [ <group> ] ]

   Create a unix domain socketnamed /dev/socket/<name> and pass

   its fd to the launchedprocess.  <type> must be"dgram", "stream" or "seqpacket".

   User and group default to0.

创建一个unix域的名为/dev/socket/<name> 的套接字,并传递它的文件描述符给已启动的进程。<type> 必须是 "dgram","stream" 或"seqpacket"。用户和组默认是0。

5.  user <username>

在启动这个服务前改变该服务的用户名。此时默认为 root。

6.  group <groupname> [<groupname> ]*

在启动这个服务前改变该服务的组名。除了(必需的)第一个组名,附加的组名通常被用于设置进程的补充组(通过setgroups函数),档案默认是root。

7.  oneshot

服务退出时不重启。

8.  class <name>

指定一个服务类。所有同一类的服务可以同时启动和停止。如果不通过class选项指定一个类,则默认为"default"类服务。

9. onrestart

当服务重启,执行一个命令(下详)。


init.rc脚本分析

在上一篇文章中说过,init将动作执行的时间划分为几个阶段,按照被执行的先后顺序依次为:early-init、init、early-boot、boot。在init进程的main()方法中被调用的先后顺序决定了它们的先后(直接原因), 根本原因是:各个阶段的任务不同导致后面的对前面的有依赖,所以这里的先后顺序是不能乱调整的。

@system/core/init/init.c



early-init主要用于设置init进程(进程号为1)的oom_adj的值,以及启动ueventd进程。oom_adj是Linux和Android中用来表示进程重要性的一个值,取值范围为[-17, 15]。在Android中系统在杀死进程时会根据oom_adj和空闲内存大小作为依据,oom_adj越大越容易被杀死。

Android将程序分成以下几类,按照重要性依次降低的顺序:

名 称oom_adj解释
FOREGROUD_APP0前 台程序,可以理解为你正在使用的程序
VISIBLE_APP1用户可见的程序
SECONDARY_SERVER2后 台服务,比如说QQ会在后台运行服务
HOME_APP4HOME,就是主界面
HIDDEN_APP7被 隐藏的程序
CONTENT_PROVIDER14内容提供者,
EMPTY_APP15 空程序,既不提供服务,也不提供内容

  1. on early-init  
  2.     # Set init and its forked children's oom_adj.  
  3.     write /proc/1/oom_adj -16  
  4.   
  5.     # Set the security context for the init process.  
  6.     # This should occur before anything else (e.g. ueventd) is started.  
  7.     setcon u:r:init:s0  
  8.   
  9.     start ueventd  
这里设置init进程的oom_adj的值为-16.这里要说明的是,我们现在分析的是init.rc文件,在文件头部我们发现还导入了其他的rc脚本。其他rc脚本中的文件结构与init.rc是类似的。在init进程解析rc脚本时,会将所有rc脚本中的配置安装执行阶段一并解析。即:init.rc中的early-init与init,${hardware}.rc中的early-init是一并解析的。

在执行完early-init以后,接下来就是init阶段。在init阶段主要用来:设置环境变量,创建和挂在文件节点。下面是init接的的不分代码截选:

@system/core/rootdir/init.environ.rc.in

  1. # set up the global environment  
  2. on init  
  3.     export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin  
  4.     export LD_LIBRARY_PATH /vendor/lib:/system/lib  
  5.     export ANDROID_BOOTLOGO 1  
  6.     export ANDROID_ROOT /system  
  7.     export ANDROID_ASSETS /system/app  
  8.     export ANDROID_DATA /data  
  9.     export ANDROID_STORAGE /storage  
  10.     export ASEC_MOUNTPOINT /mnt/asec  
  11.     export LOOP_MOUNTPOINT /mnt/obb  
  12.     export BOOTCLASSPATH %BOOTCLASSPATH%  

以前设置环境变量这一段时在init.rc中的,现在放到了init.environ.rc.in,这样代码也更清晰一些。

@system/core/rootdir/init.rc

  1. on init  
  2.   
  3. sysclktz 0  
  4.   
  5. loglevel 3  
  6.   
  7. # Backward compatibility  
  8.     symlink /system/etc /etc  
  9.     symlink /sys/kernel/debug /d  
  10.   
  11. # Right now vendor lives on the same filesystem as system,  
  12. # but someday that may change.  
  13.     symlink /system/vendor /vendor  
  14.   
  15. # Create cgroup mount point for cpu accounting  
  16.     mkdir /acct  
  17.     mount cgroup none /acct cpuacct  
  18.     mkdir /acct/uid  
  19.   
  20.     mkdir /system  
  21.     mkdir /data 0771 system system  
  22.     mkdir /cache 0770 system cache  
  23.     mkdir /config 0500 root root  


接下来是fs相关的几个过程,它们主要用于文件系统的挂载,下面是截取的一小部分代码:

  1. on post-fs  
  2.     # once everything is setup, no need to modify /  
  3.     mount rootfs rootfs / ro remount  
  4.     # mount shared so changes propagate into child namespaces  
  5.     mount rootfs rootfs / shared rec  
  6.     mount tmpfs tmpfs /mnt/secure private rec  
  7.   
  8.     # We chown/chmod /cache again so because mount is run as root + defaults  
  9.     chown system cache /cache  
  10.     chmod 0770 /cache  
  11.     # We restorecon /cache in case the cache partition has been reset.  
  12.     restorecon /cache  

如果你看过以前的版本的init.rc脚本,看到这里会想起,应该还有几行:

  1. mount yaffs2 mtd@system /system  
  2. mount yaffs2 mtd@userdata /data  
这两行用于挂载/system分区和/data分区到yaffs2文件系统。手机领域有多种不同的内存设备,其中NAND闪存设备以其低功耗、重量轻、性能佳等优良特性,受到绝大多数厂商的青睐。NAND闪存采用yaffs2文件系统。

可以看出在Android 4.4中默认已经不再使用yaffs2。在完成文件系统的创建和挂载后,完整的Android根文件系统结构如下:



接下来看一下boot部分,该部分主要用于设置应用程序终止条件,应用程序驱动目录及文件权限等。下面是一部分代码片段:

  1. on boot  
  2. # basic network init  
  3.     ifup lo  
  4.     hostname localhost  
  5.     domainname localdomain  
  6.   
  7. # set RLIMIT_NICE to allow priorities from 19 to -20  
  8.     setrlimit 13 40 40  
  9.   
  10. # Memory management.  Basic kernel parameters, and allow the high  
  11. # level system server to be able to adjust the kernel OOM driver  
  12. # parameters to match how it is managing things.  
  13.     write /proc/sys/vm/overcommit_memory 1  
  14.     write /proc/sys/vm/min_free_order_shift 4  
  15.     chown root system /sys/module/lowmemorykiller/parameters/adj  
  16.     chmod 0664 /sys/module/lowmemorykiller/parameters/adj  
  17.     chown root system /sys/module/lowmemorykiller/parameters/minfree  
  18.     chmod 0664 /sys/module/lowmemorykiller/parameters/minfree  
  19.   
  20.     class_start core  
  21.     class_start main  

在on boot部分,我们可以发现许多”on property:<name> = <value>"的代码片段,这些是根据属性的触发器,但相应的属性满足一定的条件时,就会触发相应的动作。此外,还有许多service字段,service后面第一项表示服务的名称,第二项表示服务的路径,接下来的第2行等是服务的附加内容,配合服务使用,主要包含运行权限、条件以及重启等相关选项。


解析配置文件

前面了解了init.rc脚本的相关内容,接下来我们分析一下init进程是如何解析rc脚本的。首先,在init进程的main()函数中调用init_parse_config_file()函数对属性进行解析,下面就来看一下这个函数:

  1. init_parse_config_file("/init.rc");//解析init.rc配置文件  
@system/core/init/init_parser.c

  1. int init_parse_config_file(const char *fn)  
  2. {  
  3.     char *data;  
  4.     data = read_file(fn, 0);  
  5.     if (!data) return -1;  
  6.   
  7.     parse_config(fn, data);  
  8.     DUMP();  
  9.     return 0;  
  10. }  

read_file(fn, 0)函数将fn指针指向的路径(这里即:/init.rc)所对应的文件读取到内存中,保存为字符串形式,并返回字符串在内存中的地址;然后parse_config会对文件进行解析,生成动作列表(Action List)和服务列表(Service List)。关于read_file()函数的实现在@system/core/init/util.c中。下面是parse_config()的实现:

  1. static void parse_config(const char *fn, char *s)  
  2. {  
  3.     struct parse_state state;  
  4.     struct listnode import_list;//导入链表,用于保持在init.rc中通过import导入的其他rc文件  
  5.     struct listnode *node;  
  6.     char *args[INIT_PARSER_MAXARGS];  
  7.     int nargs;  
  8.   
  9.   
  10.     nargs = 0;  
  11.     state.filename = fn;//初始化filename的值为init.rc文件  
  12.     state.line = 0;//初始化行号为0  
  13.     state.ptr = s;//初始化ptr指向s,即read_file读入到内存中的init.rc文件的首地址  
  14.     state.nexttoken = 0;//初始化nexttoken的值为0  
  15.     state.parse_line = parse_line_no_op;//初始化行解析函数  
  16.   
  17.   
  18.     list_init(&import_list);  
  19.     state.priv = &import_list;  
  20.   
  21.   
  22.     for (;;) {  
  23.         switch (next_token(&state)) {  
  24.         case T_EOF://如果返回为T_EOF,表示init.rc已经解析完成,则跳到parser_done解析import进来的其他rc脚本  
  25.             state.parse_line(&state, 0, 0);  
  26.             goto parser_done;  
  27.         case T_NEWLINE:  
  28.             state.line++;//一行读取完成后,行号加1  
  29.             if (nargs) {//如果刚才解析的一行为语法行(非注释等),则nargs的值不为0,需要对这一行进行语法解析  
  30.                 int kw = lookup_keyword(args[0]);//init.rc中每一个语法行均是以一个keyword开头的,因此args[0]即表示这一行的keyword  
  31.                 if (kw_is(kw, SECTION)) {  
  32.                     state.parse_line(&state, 0, 0);  
  33.                     parse_new_section(&state, kw, nargs, args);  
  34.                 } else {  
  35.                     state.parse_line(&state, nargs, args);  
  36.                 }  
  37.                 nargs = 0;//复位  
  38.             }  
  39.             break;  
  40.         case T_TEXT://将nexttoken解析的一个text保存到args字符串数组中,nargs的最大值为INIT_PARSER_MAXARGS(64),即init.rc中一行最多不能超过INIT_PARSER_MAXARGS个text(单词)  
  41.             if (nargs < INIT_PARSER_MAXARGS) {  
  42.                 args[nargs++] = state.text;  
  43.             }  
  44.             break;  
  45.         }  
  46.     }  
  47.   
  48.   
  49. parser_done:  
  50.     list_for_each(node, &import_list) {  
  51.          struct import *import = node_to_item(node, struct import, list);  
  52.          int ret;  
  53.   
  54.   
  55.          INFO("importing '%s'", import->filename);  
  56.          ret = init_parse_config_file(import->filename);  
  57.          if (ret)  
  58.              ERROR("could not import file '%s' from '%s'\n",  
  59.                    import->filename, fn);  
  60.     }  
  61. }  

parse_config()函数,代码虽然很短,实际上却比较复杂。接下来将对其进行详细分析。首先看一下struct parse_state的定义:

  1. struct parse_state  
  2. {  
  3.     char *ptr;//指针,指向剩余的尚未被解析的数据(即:ptr指向当前解析到的位置)  
  4.     char *text;//一行文本  
  5.     int line; //行号   
  6.     int nexttoken;//下一行的标示,T_EOF标示文件结束,T_TEXT表示需要进行解释的文本,T_NEWLINE标示一个空行或者是注释行  
  7.     void *context;//一个action或者service  
  8.     void (*parse_line)(struct parse_state *state, int nargs, char **args);//函数指针,指向当前行的解析函数  
  9.     const char *filename;//解析的rc文件  
  10.     void *priv;//执行import链表的指针  
  11. };  

next_token()以行为单位分割参数传递过来的字符串。

@system/core/init/parser.c

  1. int next_token(struct parse_state *state)  
  2. {  
  3.     char *x = state->ptr;  
  4.     char *s;  
  5.   
  6.   
  7.     if (state->nexttoken) {//nexttoken的值为0  
  8.         int t = state->nexttoken;  
  9.         state->nexttoken = 0;  
  10.         return t;  
  11.     }  
  12.   
  13.   
  14.     for (;;) {  
  15.         switch (*x) {  
  16.         case 0://到底末尾,解析完成  
  17.             state->ptr = x;  
  18.             return T_EOF;  
  19.         case '\n'://换行符,返回T_NEWLINE,表示下一个token是新的一行  
  20.             x++;  
  21.             state->ptr = x;  
  22.             return T_NEWLINE;  
  23.         case ' '://忽略空格、制表符等  
  24.         case '\t':  
  25.         case '\r':  
  26.             x++;  
  27.             continue;  
  28.         case '#'://在当前解析到的字符为#号时,将指针一直移动到#行的末尾,然后判断下一个字符是T_NEWLINE还是T_EOF  
  29.             while (*x && (*x != '\n')) x++;//注意x++,当指针移动到#行末尾时,x执行末尾的下一个字符  
  30.             if (*x == '\n') {  
  31.                 state->ptr = x+1;  
  32.                 return T_NEWLINE;  
  33.             } else {  
  34.                 state->ptr = x;  
  35.                 return T_EOF;  
  36.             }  
  37.         default:  
  38.             goto text;//解析的为普通文本  
  39.         }  
  40.     }  
  41.   
  42.   
  43. textdone://x指向一个单词的开头位置,s指向末尾位置,将s设置为0(C字符串末尾为0),即表示单词结束  
  44.     state->ptr = x;  
  45.     *s = 0;  
  46.     return T_TEXT;  
  47. text:  
  48.     state->text = s = x;  
  49. textresume:  
  50.     for (;;) {  
  51.         switch (*x) {  
  52.         case 0:  
  53.             goto textdone;  
  54.         case ' ':  
  55.         case '\t':  
  56.         case '\r':  
  57.             x++;  
  58.             goto textdone;  
  59.         case '\n':  
  60.             state->nexttoken = T_NEWLINE;  
  61.             x++;  
  62.             goto textdone;  
  63.         case '"':  
  64.             x++;  
  65.             for (;;) {  
  66.                 switch (*x) {  
  67.                 case 0:  
  68.                         /* unterminated quoted thing */  
  69.                     state->ptr = x;  
  70.                     return T_EOF;  
  71.                 case '"':  
  72.                     x++;  
  73.                     goto textresume;  
  74.                 default:  
  75.                     *s++ = *x++;  
  76.                 }  
  77.             }  
  78.             break;  
  79.         case '\\':  
  80.             x++;  
  81.             switch (*x) {  
  82.             case 0:  
  83.                 goto textdone;  
  84.             case 'n':  
  85.                 *s++ = '\n';  
  86.                 break;  
  87.             case 'r':  
  88.                 *s++ = '\r';  
  89.                 break;  
  90.             case 't':  
  91.                 *s++ = '\t';  
  92.                 break;  
  93.             case '\\':  
  94.                 *s++ = '\\';  
  95.                 break;  
  96.             case '\r':  
  97.                     /* \ <cr> <lf> -> line continuation */  
  98.                 if (x[1] != '\n') {  
  99.                     x++;  
  100.                     continue;  
  101.                 }  
  102.             case '\n':  
  103.                     /* \ <lf> -> line continuation */  
  104.                 state->line++;  
  105.                 x++;  
  106.                     /* eat any extra whitespace */  
  107.                 while((*x == ' ') || (*x == '\t')) x++;  
  108.                 continue;  
  109.             default:  
  110.                     /* unknown escape -- just copy */  
  111.                 *s++ = *x++;  
  112.             }  
  113.             continue;  
  114.         default:  
  115.             *s++ = *x++;  
  116.         }  
  117.     }  
  118.     return T_EOF;  
  119. }  

在parse_config()中通过next_token从rc脚本中解析出一行行的rc语句,下面看一下另一个重要的函数lookup_keyword()的实现:

  1. int lookup_keyword(const char *s)  
  2. {  
  3.     switch (*s++) {  
  4.     case 'c':  
  5.     if (!strcmp(s, "opy")) return K_copy;  
  6.         if (!strcmp(s, "apability")) return K_capability;  
  7.         if (!strcmp(s, "hdir")) return K_chdir;  
  8.         if (!strcmp(s, "hroot")) return K_chroot;  
  9.         if (!strcmp(s, "lass")) return K_class;  
  10.         if (!strcmp(s, "lass_start")) return K_class_start;  
  11.         if (!strcmp(s, "lass_stop")) return K_class_stop;  
  12.         if (!strcmp(s, "lass_reset")) return K_class_reset;  
  13.         if (!strcmp(s, "onsole")) return K_console;  
  14.         if (!strcmp(s, "hown")) return K_chown;  
  15.         if (!strcmp(s, "hmod")) return K_chmod;  
  16.         if (!strcmp(s, "ritical")) return K_critical;  
  17.         break;  
  18.     case 'd':  
  19.         if (!strcmp(s, "isabled")) return K_disabled;  
  20.         if (!strcmp(s, "omainname")) return K_domainname;  
  21.         break;  
  22.     case 'e':  
  23.         if (!strcmp(s, "xec")) return K_exec;  
  24.         if (!strcmp(s, "xport")) return K_export;  
  25.         break;  
  26.     case 'g':  
  27.         if (!strcmp(s, "roup")) return K_group;  
  28.         break;  
  29.     case 'h':  
  30.         if (!strcmp(s, "ostname")) return K_hostname;  
  31.         break;  
  32.     case 'i':  
  33.         if (!strcmp(s, "oprio")) return K_ioprio;  
  34.         if (!strcmp(s, "fup")) return K_ifup;  
  35.         if (!strcmp(s, "nsmod")) return K_insmod;  
  36.         if (!strcmp(s, "mport")) return K_import;  
  37.         break;  
  38.     case 'k':  
  39.         if (!strcmp(s, "eycodes")) return K_keycodes;  
  40.         break;  
  41.     case 'l':  
  42.         if (!strcmp(s, "oglevel")) return K_loglevel;  
  43.         if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;  
  44.         break;  
  45.     case 'm':  
  46.         if (!strcmp(s, "kdir")) return K_mkdir;  
  47.         if (!strcmp(s, "ount_all")) return K_mount_all;  
  48.         if (!strcmp(s, "ount")) return K_mount;  
  49.         break;  
  50.     case 'o':  
  51.         if (!strcmp(s, "n")) return K_on;  
  52.         if (!strcmp(s, "neshot")) return K_oneshot;  
  53.         if (!strcmp(s, "nrestart")) return K_onrestart;  
  54.         break;  
  55.     case 'p':  
  56.         if (!strcmp(s, "owerctl")) return K_powerctl;  
  57.     case 'r':  
  58.         if (!strcmp(s, "estart")) return K_restart;  
  59.         if (!strcmp(s, "estorecon")) return K_restorecon;  
  60.         if (!strcmp(s, "mdir")) return K_rmdir;  
  61.         if (!strcmp(s, "m")) return K_rm;  
  62.         break;  
  63.     case 's':  
  64.         if (!strcmp(s, "eclabel")) return K_seclabel;  
  65.         if (!strcmp(s, "ervice")) return K_service;  
  66.         if (!strcmp(s, "etcon")) return K_setcon;  
  67.         if (!strcmp(s, "etenforce")) return K_setenforce;  
  68.         if (!strcmp(s, "etenv")) return K_setenv;  
  69.         if (!strcmp(s, "etkey")) return K_setkey;  
  70.         if (!strcmp(s, "etprop")) return K_setprop;  
  71.         if (!strcmp(s, "etrlimit")) return K_setrlimit;  
  72.         if (!strcmp(s, "etsebool")) return K_setsebool;  
  73.         if (!strcmp(s, "ocket")) return K_socket;  
  74.         if (!strcmp(s, "tart")) return K_start;  
  75.         if (!strcmp(s, "top")) return K_stop;  
  76.         if (!strcmp(s, "wapon_all")) return K_swapon_all;  
  77.         if (!strcmp(s, "ymlink")) return K_symlink;  
  78.         if (!strcmp(s, "ysclktz")) return K_sysclktz;  
  79.         break;  
  80.     case 't':  
  81.         if (!strcmp(s, "rigger")) return K_trigger;  
  82.         break;  
  83.     case 'u':  
  84.         if (!strcmp(s, "ser")) return K_user;  
  85.         break;  
  86.     case 'w':  
  87.         if (!strcmp(s, "rite")) return K_write;  
  88.         if (!strcmp(s, "ait")) return K_wait;  
  89.         break;  
  90.     }  
  91.     return K_UNKNOWN;  
  92. }  
lookup_keyword()主要用解析出args中的关键字,这个函数本身没有什么特别,也非常简单,但是其实现方法在我们自己实现类似通过switch等的查找判断时是值得借鉴的,即:先通过单词的首字母将内容分组,在定位到哪一个组后再依次比较。这样就减少了程序中比较的次数,提高了效率。
  1. case T_NEWLINE:  
  2.     state.line++;//一行读取完成后,行号加1  
  3.     if (nargs) {//如果刚才解析的一行为语法行(非注释等),则nargs的值不为0,需要对这一行进行语法解析  
  4.         int kw = lookup_keyword(args[0]);//init.rc中每一个语法行均是以一个keyword开头的,因此args[0]即表示这一行的keyword  
  5.         if (kw_is(kw, SECTION)) {  
  6.             state.parse_line(&state, 0, 0);  
  7.             parse_new_section(&state, kw, nargs, args);  
  8.         } else {  
  9.             state.parse_line(&state, nargs, args);  
  10.         }  
  11.         nargs = 0;//复位  
  12.     }  
  13.     break;  

在parse_config()中,在找的keyword以后,接下来会判断这个keyword是否是section,是则走解析section的逻辑,否则走其他逻辑。下面我们看一下kw_is的实现:

  1. #define kw_is(kw, type) (keyword_info[kw].flags & (type))  
可以看出kw_is只不过是一个宏定义,这里又引出了keyword_info,下面让我们一起来看一下keyword的相关定义:

关键字定义

@system/core/init/keywords.h

  1. #ifndef KEYWORD//如果没有定义KEYWORD则执行下面的分支  
  2. //声明一些函数,这些函数即Action的执行函数  
  3. int do_chroot(int nargs, char **args);  
  4. int do_chdir(int nargs, char **args);  
  5. int do_class_start(int nargs, char **args);  
  6. int do_class_stop(int nargs, char **args);  
  7. int do_class_reset(int nargs, char **args);  
  8. int do_domainname(int nargs, char **args);  
  9. int do_exec(int nargs, char **args);  
  10. int do_export(int nargs, char **args);  
  11. int do_hostname(int nargs, char **args);  
  12. int do_ifup(int nargs, char **args);  
  13. int do_insmod(int nargs, char **args);  
  14. int do_mkdir(int nargs, char **args);  
  15. int do_mount_all(int nargs, char **args);  
  16. int do_mount(int nargs, char **args);  
  17. int do_powerctl(int nargs, char **args);  
  18. int do_restart(int nargs, char **args);  
  19. int do_restorecon(int nargs, char **args);  
  20. int do_rm(int nargs, char **args);  
  21. int do_rmdir(int nargs, char **args);  
  22. int do_setcon(int nargs, char **args);  
  23. int do_setenforce(int nargs, char **args);  
  24. int do_setkey(int nargs, char **args);  
  25. int do_setprop(int nargs, char **args);  
  26. int do_setrlimit(int nargs, char **args);  
  27. int do_setsebool(int nargs, char **args);  
  28. int do_start(int nargs, char **args);  
  29. int do_stop(int nargs, char **args);  
  30. int do_swapon_all(int nargs, char **args);  
  31. int do_trigger(int nargs, char **args);  
  32. int do_symlink(int nargs, char **args);  
  33. int do_sysclktz(int nargs, char **args);  
  34. int do_write(int nargs, char **args);  
  35. int do_copy(int nargs, char **args);  
  36. int do_chown(int nargs, char **args);  
  37. int do_chmod(int nargs, char **args);  
  38. int do_loglevel(int nargs, char **args);  
  39. int do_load_persist_props(int nargs, char **args);  
  40. int do_wait(int nargs, char **args);  
  41. #define __MAKE_KEYWORD_ENUM__//定义一个宏  
  42. /* 
  43.  * 定义KEYWORD宏,这里KEYWORD宏中有四个参数,其各自的含义如下: 
  44.  * symbol表示keyword的名称(即init.rc中的关键字); 
  45.  * flags表示keyword的类型,包括SECTION、COMMAND和OPTION三种类型,其定义在init_parser.c中; 
  46.  * nargs表示参数的个数,即:该keyword需要几个参数 
  47.  * func表示该keyword所对应的处理函数。 
  48.  * 
  49.  * KEYWORD宏虽然有四个参数,但是这里只用到了symbol,其中K_##symbol中的##表示连接的意思, 
  50.  * 即最后的得到的值为K_symbol。 
  51.  */  
  52. #define KEYWORD(symbol, flags, nargs, func) K_##symbol,  
  53. enum {  
  54.     K_UNKNOWN,  
  55. #endif  
  56.     KEYWORD(capability,  OPTION,  0, 0)//根据上面KEYWORD的宏定义,这一行就变成了K_capability,  
  57.     KEYWORD(chdir,       COMMAND, 1, do_chdir)//key_chdir,后面的依次类推  
  58.     KEYWORD(chroot,      COMMAND, 1, do_chroot)  
  59.     KEYWORD(class,       OPTION,  0, 0)  
  60.     KEYWORD(class_start, COMMAND, 1, do_class_start)  
  61.     KEYWORD(class_stop,  COMMAND, 1, do_class_stop)  
  62.     KEYWORD(class_reset, COMMAND, 1, do_class_reset)  
  63.     KEYWORD(console,     OPTION,  0, 0)  
  64.     KEYWORD(critical,    OPTION,  0, 0)  
  65.     KEYWORD(disabled,    OPTION,  0, 0)  
  66.     KEYWORD(domainname,  COMMAND, 1, do_domainname)  
  67.     KEYWORD(exec,        COMMAND, 1, do_exec)  
  68.     KEYWORD(export,      COMMAND, 2, do_export)  
  69.     KEYWORD(group,       OPTION,  0, 0)  
  70.     KEYWORD(hostname,    COMMAND, 1, do_hostname)  
  71.     KEYWORD(ifup,        COMMAND, 1, do_ifup)  
  72.     KEYWORD(insmod,      COMMAND, 1, do_insmod)  
  73.     KEYWORD(import,      SECTION, 1, 0)  
  74.     KEYWORD(keycodes,    OPTION,  0, 0)  
  75.     KEYWORD(mkdir,       COMMAND, 1, do_mkdir)  
  76.     KEYWORD(mount_all,   COMMAND, 1, do_mount_all)  
  77.     KEYWORD(mount,       COMMAND, 3, do_mount)  
  78.     KEYWORD(on,          SECTION, 0, 0)  
  79.     KEYWORD(oneshot,     OPTION,  0, 0)  
  80.     KEYWORD(onrestart,   OPTION,  0, 0)  
  81.     KEYWORD(powerctl,    COMMAND, 1, do_powerctl)  
  82.     KEYWORD(restart,     COMMAND, 1, do_restart)  
  83.     KEYWORD(restorecon,  COMMAND, 1, do_restorecon)  
  84.     KEYWORD(rm,          COMMAND, 1, do_rm)  
  85.     KEYWORD(rmdir,       COMMAND, 1, do_rmdir)  
  86.     KEYWORD(seclabel,    OPTION,  0, 0)  
  87.     KEYWORD(service,     SECTION, 0, 0)  
  88.     KEYWORD(setcon,      COMMAND, 1, do_setcon)  
  89.     KEYWORD(setenforce,  COMMAND, 1, do_setenforce)  
  90.     KEYWORD(setenv,      OPTION,  2, 0)  
  91.     KEYWORD(setkey,      COMMAND, 0, do_setkey)  
  92.     KEYWORD(setprop,     COMMAND, 2, do_setprop)  
  93.     KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)  
  94.     KEYWORD(setsebool,   COMMAND, 2, do_setsebool)  
  95.     KEYWORD(socket,      OPTION,  0, 0)  
  96.     KEYWORD(start,       COMMAND, 1, do_start)  
  97.     KEYWORD(stop,        COMMAND, 1, do_stop)  
  98.     KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)  
  99.     KEYWORD(trigger,     COMMAND, 1, do_trigger)  
  100.     KEYWORD(symlink,     COMMAND, 1, do_symlink)  
  101.     KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)  
  102.     KEYWORD(user,        OPTION,  0, 0)  
  103.     KEYWORD(wait,        COMMAND, 1, do_wait)  
  104.     KEYWORD(write,       COMMAND, 2, do_write)  
  105.     KEYWORD(copy,        COMMAND, 2, do_copy)  
  106.     KEYWORD(chown,       COMMAND, 2, do_chown)  
  107.     KEYWORD(chmod,       COMMAND, 2, do_chmod)  
  108.     KEYWORD(loglevel,    COMMAND, 1, do_loglevel)  
  109.     KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)  
  110.     KEYWORD(ioprio,      OPTION,  0, 0)  
  111. #ifdef __MAKE_KEYWORD_ENUM__  
  112.     KEYWORD_COUNT,  
  113. };  
  114. #undef __MAKE_KEYWORD_ENUM__  
  115. #undef KEYWORD//取消KEYWORD宏的定义  
  116. #endif  

看一下keyword在init_parse.c中是如何被使用的:

  1. #include "keywords.h"  
  2.   
  3. #define KEYWORD(symbol, flags, nargs, func) \  
  4.     [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },  
  5.   
  6. struct {  
  7.     const char *name;//关键字的名称  
  8.     int (*func)(int nargs, char **args);//对应关键字的处理函数  
  9.     unsigned char nargs;//参数个数,每个关键字的参数个数是固定的  
  10.     unsigned char flags;//关键字属性,包括:SECTION、OPTION和COMMAND,其中COMMAND有对应的处理函数,见keyword的定义。  
  11. } keyword_info[KEYWORD_COUNT] = {  
  12.     [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },  
  13. #include "keywords.h"  
  14. };  
  15. #undef KEYWORD  
  16.   
  17. #define kw_is(kw, type) (keyword_info[kw].flags & (type))  
  18. #define kw_name(kw) (keyword_info[kw].name)  
  19. #define kw_func(kw) (keyword_info[kw].func)  
  20. #define kw_nargs(kw) (keyword_info[kw].nargs)  
从上面的代码我们看到一个很有意思的地方,keyword.h头文件被包含引用了两次。

  • 第一次包含keywords.h时,它声明了一些诸如do_class_start的函数,另外还定义了一个枚举,枚举值为K_class、K_mkdir等关键字。
  • 第二次包含keywords.h后,得到了keyword_info结构体数组,这个keyword_info结构体数组以前定义的枚举值为索引,存储对应的关键字信息。
flags的取值也在init_parse.c中定义:

  1. #define SECTION 0x01  
  2. #define COMMAND 0x02  
  3. #define OPTION  0x04  

在了解了keyword后,下面我们继续来分析rc脚本的解析,让我们回到之前的代码,继续分析。

  1. case T_NEWLINE:  
  2.     state.line++;//一行读取完成后,行号加1  
  3.     if (nargs) {//如果刚才解析的一行为语法行(非注释等),则nargs的值不为0,需要对这一行进行语法解析  
  4.         int kw = lookup_keyword(args[0]);//init.rc中每一个语法行均是以一个keyword开头的,因此args[0]即表示这一行的keyword  
  5.         if (kw_is(kw, SECTION)) {  
  6.             state.parse_line(&state, 0, 0);  
  7.             parse_new_section(&state, kw, nargs, args);  
  8.         } else {  
  9.             state.parse_line(&state, nargs, args);  
  10.         }  
  11.         nargs = 0;//复位  
  12.     }  
  13.     break;  
解析section的函数为parse_new_section,其实现为:

  1. void parse_new_section(struct parse_state *state, int kw,  
  2.                        int nargs, char **args)  
  3. {  
  4.     printf("[ %s %s ]\n", args[0],  
  5.            nargs > 1 ? args[1] : "");  
  6.     switch(kw) {  
  7.     case K_service://解析Service  
  8.         state->context = parse_service(state, nargs, args);//当service_list中不存在同名service时,执行新加入service_list中的service  
  9.         if (state->context) {//service为新增加的service时,即:<span style="font-family: Arial, Helvetica, sans-serif;">service_list中不存在同名service</span>  
  10.             state->parse_line = parse_line_service;//制定解析service行的函数为<span style="font-family: Arial, Helvetica, sans-serif;">parse_line_service</span>  
  11.             return;  
  12.         }  
  13.         break;  
  14.     case K_on://解析section  
  15.         state->context = parse_action(state, nargs, args);  
  16.         if (state->context) {  
  17.             state->parse_line = parse_line_action;  
  18.             return;  
  19.         }  
  20.         break;  
  21.     case K_import://解析import  
  22.         parse_import(state, nargs, args);  
  23.         break;  
  24.     }  
  25.     state->parse_line = parse_line_no_op;  
  26. }  

先看一下service的解析:

  1. static void *parse_service(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct service *svc;//保持Service相关信息  
  4.     if (nargs < 3) {  
  5.         parse_error(state, "services must have a name and a program\n");  
  6.         return 0;  
  7.     }  
  8.     if (!valid_name(args[1])) {  
  9.         parse_error(state, "invalid service name '%s'\n", args[1]);  
  10.         return 0;  
  11.     }  
  12.    //service_list中是否已存在同名service<span style="white-space:pre">    </span>  
  13.     svc = service_find_by_name(args[1]);  
  14.     if (svc) {//<span style="font-family: Arial, Helvetica, sans-serif;">如果已存在同名service则直接返回,不再做其他操作</span>  
  15.         parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);  
  16.         return 0;  
  17.     }  
  18.   
  19.     nargs -= 2;  
  20.     svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);  
  21.     if (!svc) {  
  22.         parse_error(state, "out of memory\n");  
  23.         return 0;  
  24.     }  
  25.     svc->name = args[1];  
  26.     svc->classname = "default";//设置classname为“default”  
  27.     memcpy(svc->args, args + 2, sizeof(char*) * nargs);  
  28.     svc->args[nargs] = 0;  
  29.     svc->nargs = nargs;  
  30.     svc->onrestart.name = "onrestart";  
  31.     list_init(&svc->onrestart.commands);  
  32.     list_add_tail(&service_list, &svc->slist);//将service添加到全局链表service_list中  
  33.     return svc;  
  34. }  
init中使用了一个叫做service的结构体来保存与service相关的信息。
@system/core/init/init.h

  1. struct service {  
  2.         /* list of all services */  
  3.     struct listnode slist;//双向链表  
  4.   
  5.     const char *name;//service的名字  
  6.     const char *classname;//service所属class的名字,默认是“default”  
  7.   
  8.     unsigned flags;//service的属性  
  9.     pid_t pid;//进程号  
  10.     time_t time_started;    /* time of last start 上一次启动的时间*/  
  11.     time_t time_crashed;    /* first crash within inspection window 第一次死亡的时间*/  
  12.     int nr_crashed;         /* number of times crashed within window 死亡次数*/  
  13.       
  14.     uid_t uid;  
  15.     gid_t gid;  
  16.     gid_t supp_gids[NR_SVC_SUPP_GIDS];  
  17.     size_t nr_supp_gids;  
  18.   
  19.     char *seclabel;  
  20.   
  21.     struct socketinfo *sockets;//有些service需要使用socket,socketinfo用来描述socket相关信息  
  22.     struct svcenvinfo *envvars;//service一般运行在一个单独的进程中,envvars用来描述创建这个进程时所需的环境变量信息  
  23.     //关键字onrestart标示一个OPTION,可是onrestart后面一般跟着COMMAND,下面这个action结构体可用来存储command信息  
  24.     struct action onrestart;  /* Actions to execute on restart. */  
  25.       
  26.     /* keycodes for triggering this service via /dev/keychord */  
  27.     int *keycodes;  
  28.     int nkeycodes;  
  29.     int keychord_id;  
  30.   
  31.     int ioprio_class;  
  32.     int ioprio_pri;  
  33.   
  34.     int nargs;//参数个数  
  35.     /* "MUST BE AT THE END OF THE STRUCT" */  
  36.     char *args[1];//用于存储参数  
  37. }; /*     ^-------'args' MUST be at the end of this struct! */  

从parse_service函数可以看出,它的作用就是讲service添加到service_list列表中,并制定解析函数为parse_line_service,也就是说具体的service的解析靠的是parse_line_service方法。

  1. static void parse_line_service(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct service *svc = state->context;  
  4.     struct command *cmd;  
  5.     int i, kw, kw_nargs;  
  6.   
  7.     if (nargs == 0) {  
  8.         return;  
  9.     }  
  10.   
  11.     svc->ioprio_class = IoSchedClass_NONE;  
  12.   
  13.     kw = lookup_keyword(args[0]);  
  14.     switch (kw) {  
  15.     case K_capability:  
  16.         break;  
  17.     case K_class:  
  18.         if (nargs != 2) {  
  19.             parse_error(state, "class option requires a classname\n");  
  20.         } else {  
  21.             svc->classname = args[1];  
  22.         }  
  23.         break;  
  24.     case K_console:  
  25.         svc->flags |= SVC_CONSOLE;  
  26.         break;  
  27.     case K_disabled:  
  28.         svc->flags |= SVC_DISABLED;  
  29.         svc->flags |= SVC_RC_DISABLED;  
  30.         break;  
  31.     case K_ioprio:  
  32.         if (nargs != 3) {  
  33.             parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");  
  34.         } else {  
  35.             svc->ioprio_pri = strtoul(args[2], 0, 8);  
  36.   
  37.             if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {  
  38.                 parse_error(state, "priority value must be range 0 - 7\n");  
  39.                 break;  
  40.             }  
  41.   
  42.             if (!strcmp(args[1], "rt")) {  
  43.                 svc->ioprio_class = IoSchedClass_RT;  
  44.             } else if (!strcmp(args[1], "be")) {  
  45.                 svc->ioprio_class = IoSchedClass_BE;  
  46.             } else if (!strcmp(args[1], "idle")) {  
  47.                 svc->ioprio_class = IoSchedClass_IDLE;  
  48.             } else {  
  49.                 parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");  
  50.             }  
  51.         }  
  52.         break;  
  53.     case K_group:  
  54.         if (nargs < 2) {  
  55.             parse_error(state, "group option requires a group id\n");  
  56.         } else if (nargs > NR_SVC_SUPP_GIDS + 2) {  
  57.             parse_error(state, "group option accepts at most %d supp. groups\n",  
  58.                         NR_SVC_SUPP_GIDS);  
  59.         } else {  
  60.             int n;  
  61.             svc->gid = decode_uid(args[1]);  
  62.             for (n = 2; n < nargs; n++) {  
  63.                 svc->supp_gids[n-2] = decode_uid(args[n]);  
  64.             }  
  65.             svc->nr_supp_gids = n - 2;  
  66.         }  
  67.         break;  
  68.     case K_keycodes:  
  69.         if (nargs < 2) {  
  70.             parse_error(state, "keycodes option requires atleast one keycode\n");  
  71.         } else {  
  72.             svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));  
  73.             if (!svc->keycodes) {  
  74.                 parse_error(state, "could not allocate keycodes\n");  
  75.             } else {  
  76.                 svc->nkeycodes = nargs - 1;  
  77.                 for (i = 1; i < nargs; i++) {  
  78.                     svc->keycodes[i - 1] = atoi(args[i]);  
  79.                 }  
  80.             }  
  81.         }  
  82.         break;  
  83.     case K_oneshot:  
  84.         svc->flags |= SVC_ONESHOT;  
  85.         break;  
  86.     case K_onrestart:  
  87.         nargs--;  
  88.         args++;  
  89.         kw = lookup_keyword(args[0]);  
  90.         if (!kw_is(kw, COMMAND)) {  
  91.             parse_error(state, "invalid command '%s'\n", args[0]);  
  92.             break;  
  93.         }  
  94.         kw_nargs = kw_nargs(kw);  
  95.         if (nargs < kw_nargs) {  
  96.             parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,  
  97.                 kw_nargs > 2 ? "arguments" : "argument");  
  98.             break;  
  99.         }  
  100.   
  101.         cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);  
  102.         cmd->func = kw_func(kw);  
  103.         cmd->nargs = nargs;  
  104.         memcpy(cmd->args, args, sizeof(char*) * nargs);  
  105.         list_add_tail(&svc->onrestart.commands, &cmd->clist);  
  106.         break;  
  107.     case K_critical:  
  108.         svc->flags |= SVC_CRITICAL;  
  109.         break;  
  110.     case K_setenv: { /* name value */  
  111.         struct svcenvinfo *ei;  
  112.         if (nargs < 2) {  
  113.             parse_error(state, "setenv option requires name and value arguments\n");  
  114.             break;  
  115.         }  
  116.         ei = calloc(1, sizeof(*ei));  
  117.         if (!ei) {  
  118.             parse_error(state, "out of memory\n");  
  119.             break;  
  120.         }  
  121.         ei->name = args[1];  
  122.         ei->value = args[2];  
  123.         ei->next = svc->envvars;  
  124.         svc->envvars = ei;  
  125.         break;  
  126.     }  
  127.     case K_socket: {/* name type perm [ uid gid ] */  
  128.         struct socketinfo *si;  
  129.         if (nargs < 4) {  
  130.             parse_error(state, "socket option requires name, type, perm arguments\n");  
  131.             break;  
  132.         }  
  133.         if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")  
  134.                 && strcmp(args[2],"seqpacket")) {  
  135.             parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");  
  136.             break;  
  137.         }  
  138.         si = calloc(1, sizeof(*si));  
  139.         if (!si) {  
  140.             parse_error(state, "out of memory\n");  
  141.             break;  
  142.         }  
  143.         si->name = args[1];  
  144.         si->type = args[2];  
  145.         si->perm = strtoul(args[3], 0, 8);  
  146.         if (nargs > 4)  
  147.             si->uid = decode_uid(args[4]);  
  148.         if (nargs > 5)  
  149.             si->gid = decode_uid(args[5]);  
  150.         si->next = svc->sockets;  
  151.         svc->sockets = si;  
  152.         break;  
  153.     }  
  154.     case K_user:  
  155.         if (nargs != 2) {  
  156.             parse_error(state, "user option requires a user id\n");  
  157.         } else {  
  158.             svc->uid = decode_uid(args[1]);  
  159.         }  
  160.         break;  
  161.     case K_seclabel:  
  162.         if (nargs != 2) {  
  163.             parse_error(state, "seclabel option requires a label string\n");  
  164.         } else {  
  165.             svc->seclabel = args[1];  
  166.         }  
  167.         break;  
  168.   
  169.     default:  
  170.         parse_error(state, "invalid option '%s'\n", args[0]);  
  171.     }  
  172. }  
可以看出parse_line_service中会根据keyword找的对应的keyword的处理函数,具体进程处理。
section的处理与service类似,通过分析init.rc的解析过程,我们知道,所谓的解析就是将rc脚本中的内容通过解析,填充到service_list和action_list中去。那他们是在哪里进行调用的呢,让我们回忆一下init进程中main函数的实现。

  1. INFO("reading config file\n");  
  2. init_parse_config_file("/init.rc");//解析init.rc配置文件  
  3.   
  4. action_for_each_trigger("early-init", action_add_queue_tail);  
  5.   
  6. queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  7. queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  8. queue_builtin_action(keychord_init_action, "keychord_init");  
  9. queue_builtin_action(console_init_action, "console_init");  
  10.   
  11. /* execute all the boot actions to get us started */  
  12. action_for_each_trigger("init", action_add_queue_tail);  
  13.   
  14. /* skip mounting filesystems in charger mode */  
  15. if (!is_charger) {  
  16.     action_for_each_trigger("early-fs", action_add_queue_tail);  
  17.     action_for_each_trigger("fs", action_add_queue_tail);  
  18.     action_for_each_trigger("post-fs", action_add_queue_tail);  
  19.     action_for_each_trigger("post-fs-data", action_add_queue_tail);  
  20. }  

OK,到这里init.rc脚本的解析就完了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值