深入理解init_2-----解析配置文件init.rc(基于Android 2.2,代码源于Google)
  HvTJUzsxOBtS 2023年11月25日 24 0


深入理解init_2—–解析配置文件init.rc

1、 parse_config_file函数解析配置文件

根据上文我们可知,在init中会解析两个配置文件,其中一个是系统配置文件init.rc,另外一个是与平台相关的配置文件。以HTC G7手机为例,这个配置文件名为init.Bravo.rc,其中bravo是硬件平台的名称。对这两个配置文件进行解析,调用的是同一个parse_config_file函数。下面我们来看一下这个函数,在分析中以init.rc为主。

深入理解init_2-----解析配置文件init.rc(基于Android 2.2,代码源于Google)_android

解析配置文件 init.rc 主要流程图

2、寻找parse_config_file函数

2.1、文件位置

system/core/init/Parser.c

2.2、关键代码分析

int parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);    //读取配置文件的内容,这个文件是init.rc
    if (!data) return -1;

    parse_config(fn, data);       //调用Parser_config做真正的解析
    DUMP();
    return 0;
}

// Read_file函数读取完文件的内容后,将调用parse_config函数进行解析,这个函数代码如下:
static void parse_config(const char *fn, char *s)
{
    struct parse_state state;
    char *args[SVC_MAXARGS];
    int nargs;

    nargs = 0;
    state.filename = fn;
    state.line = 1;
    state.ptr = s;
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;      //设置解析函数,不同的内容用不同的解析函数
    for (;;) {
        switch (next_token(&state)) {
        case T_EOF:
            state.parse_line(&state, 0, 0);
            return;
        case T_NEWLINE:
            if (nargs) {
                int kw = lookup_keyword(args[0]);   //得到关键字的类型
                if (kw_is(kw, SECTION)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);   //解析这个SECTION
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < SVC_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }
}
上面就是Parse_config函数,代码虽短,实际却比较复杂。从整体来说,parse_config首先会找到配置文件的一个section,然后针对不同的section使用不同的解析函数来解析。那么,什么是配置文件的section呢?这和init.rc的组织结构有关,现在不必着急去看init.rc我们先以下内容:
Init中关键字的定义

3、Init.rc中关键字的定义

Keywords.h这个文件定义了init中使用的关键字,先来看一下这个文件

3.1、文件位置

System/core/init/keywords.h

3.2、关键代码分析

#ifndef KEYWORD   //如果没有定义下面的KEYWORD宏,则走下面的分支
int do_chroot(int nargs, char **args);
(省略部分代码······)
int do_restart(int nargs, char **args);
(省略部分代码······)
int do_device(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__   //定义一个宏

/*定义KEYWORD宏,虽然有四个参数,但是这里只用第一个,其中K_##symbol中的## 表示链接的意思,即最后得到的值为K_symbol。Symbol就是init.rc中的关键字*/
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {       //定义一个枚举变量,这个枚举变量定义了各个关键字的枚举值
    K_UNKNOWN,
#endif
    KEYWORD(capability,  OPTION,  0, 0)
    KEYWORD(chdir,       COMMAND, 1, do_chdir)
    KEYWORD(chroot,      COMMAND, 1, do_chroot)
    KEYWORD(class,       OPTION,  0, 0)      //根据上面KEYWORD的定义,这里得到一个枚举值K_class
    KEYWORD(class_start, COMMAND, 1, do_class_start)
(省略部分代码······)
    KEYWORD(ioprio,      OPTION,  0, 0)
#ifdef __MAKE_KEYWORD_ENUM__
    KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD  //取消KEYWORD的定义
#endif
Keywords.h在接下来的代码中我们看它如何使用的。

跳转到 parser.c文件

4 跳转到parser.c文件

4.1、文件位置

System/core/init/Parser.c

4.2、关键代码分析

(省略部分代码······)
/*parser.c中将包含Keywords.h头文件,而且不止一次
第一次包含Ketwords.h,根据Keywords.h的代码可知,我们首先得到一个枚举定义*/
#include "keywords.h"
/*重新定义KEYWORD的宏,这次四个参数都用上了,看起来好象是一个结构体,其中#symbol表示一个字符串,其值为“symbol”*/
#define KEYWORD(symbol, flags, nargs, func) \
    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

struct {
    const char *name;   //关键字的名字
    int (*func)(int nargs, char **args);   //对应关键字的处理函数
    unsigned char nargs;     //参数个数,每个关键字的参数个数固定
//每个关键字的属性有三种:COMMAND、OPTION和SECTION,其中COMMAND有对应的处理函数
    unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
[ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
/*第二次包含keywords.h,由于已经重新定义了KEYWORD宏,所以以前作为枚举值的关键字,现在变成了keyword_info数组的索引了*/
#include "keywords.h"    
};
#undef KEYWORD
//一些辅助宏,帮助我们快速操作keyword_info 中的内容
#define kw_is(kw, type) (keyword_info[kw].flags & (type))
#define kw_name(kw) (keyword_info[kw].name)
#define kw_func(kw) (keyword_info[kw].func)
#define kw_nargs(kw) (keyword_info[kw].nargs)
(省略部分代码······)
现在我们了解了parser.c中keywords.h的工作了吧,它主要做了两件事情:

1) 第一次包含keywords.h时,它声明了一些诸如 do_class_star的函数,另外还定义了一个枚举,枚举值为K_class,K_mkdir等关键字。
2) 第二次包含keywords.h后,得到一个keyword_info结构体数组,这个keyword_结构体数组以前面定义的枚举值为索引,存储对应的关键字的信息,这些信息,包括关键字名称、处理函数、处理函数的参数个数、以及属性。

现在,关键字信息中最重要的就是symbol和flags,什么样的关键字被认为是section?根据keywords.h的定义,当symbol为on或者为service的时候表示section;

KEYWORD(on, SECTION, 0, 0)
KEYWORD(service, SECTION, 0, 0)

有了上面的了解,现在我们再来看init.rc

5、解析init.rc文件

5.1、文件位置

System/core/rootdir/init.rc

5.2、关键代码分析

on init     #on 关键字标示一个section,对应的名字是“init”
#下面所有的内容都属于这个section,知道下一个section开始工作时。
sysclktz 0

loglevel 3

# setup the global environment
    export PATH /sbin:/system/sbin:/system/bin:/system/xbin
    export LD_LIBRARY_PATH /system/lib
    export ANDROID_BOOTLOGO 1   #根据keyword.h的定义可知,export表示COMMAND。

(省略部分代码······)
on boot     #这是一个新的section ,名为“boot”
# basic network init  
    ifup lo     #这是一个COMMAND
    hostname localhost
    domainname localdomain


class_start default //class_start 也是一个COMMAND,对应函数do_class_start.

(省略部分代码······)
#下面这个section的意思是:待属性persist.service.adb.enable的值变成1后,需要执行下面的COMMAND,这个COMMAND是start adbd  .

on property:persist.service.adb.enable=1
    start adbd              #start  是一个COMMAND

on property:persist.service.adb.enable=0
    stop adbd

(省略部分代码······)
#service 也是section的标示,对应section的名称为“zygote”。
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666    #socket 关键字表示OPTION
    onrestart write /sys/android_power/request_state wake       #onrestart  也是OPTION 
    onrestart write /sys/power/state on
    onrestart restart media

# 一个service(同时也是一个section),名为“media”
service media /system/bin/mediaserver
    user media
    group system audio camera graphics inet net_bt net_bt_admin net_raw
    ioprio rt 4
(省略部分代码······)

从上面对init.rc的分析可知:
1)一个section 的内容,可以从这个标志section的关键字开始,直到下一个标识section的地方结束。
2)init.rc 中出现名为boot和init的section,这个boot和init就是前面介绍的4个动作执行阶段中的boot和init。也就是说在boot阶段执行的动作,都是由boot这个section定义的。

另外可以发现,zygote被放置在一个service section中,下面zygote这个section为例,介绍service是如何解析的。

文档参考:

整理抄录自 — 《深入理解Android卷1》,邓凡平著。


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月25日 0

暂无评论

推荐阅读
HvTJUzsxOBtS