Android Init进程解析init.rc

本文详细介绍了Android系统的init进程解析init.rc文件的过程。通过parse_config函数,解析init.rc中的Section、Command和Option,如service、import等关键字。解析过程中,init.c使用keywords.h定义关键字行为,并实例化相应结构体。解析完成后,对import的文件进行递归解析,将配置映射到C++对象中。

主要是通过该函数实现解析:在init.c中

init_parse_config_file("/init.rc");
int init_parse_config_file(const char *fn)
{
    char *data;
    //读取文件的内容
    data = read_file(fn, 0);
    if (!data) return -1;
    //真正解析的函数
    parse_config(fn, data);
    DUMP();
    return 0;
}
static void parse_config(const char *fn, char *s)
{
    struct parse_state state;
    struct listnode import_list;
    struct listnode *node;
    char *args[INIT_PARSER_MAXARGS];
    int nargs;

    nargs = 0;
    state.filename = fn;
    state.line = 0;
    state.ptr = s;
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;

    list_init(&import_list);
    state.priv = &import_list;

    for (;;) {
        switch (next_token(&state)) {
        case T_EOF:
            state.parse_line(&state, 0, 0);
            goto parser_done;
        case T_NEWLINE:
            state.line++;
            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);
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }

parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         int ret;

         INFO("importing '%s'", import->filename);
         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}

parse_config()函数,位于init_parser.c,在该文件的开头,对一些关键字进行了一些定义,主要通过keywords.h来完成。第一次include keywords.h的时候,是用来定义枚举类型,和函数(结构体中有一个(*func)的成员,在keywords.h中定义了,那个关键字调用哪个函数,如KEYWORD(enable,      COMMAND, 1, do_enable),定义了,enable关键字的func函数是do_eable),第二次include keywords.h的时候,是用来填充 keyword_inof这个结构体数组。

#include "keywords.h"

#define KEYWORD(symbol, flags, nargs, func) \
    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

static struct {
    const char *name;
    int (*func)(int nargs, char **args);
    unsigned char nargs;
    unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
#include "keywords.h"
};
#undef KEYWORD

在keywords.h中,将关键字分成了三类,分别是:Section、Command、Option.以init.rc中的一段来看parser_config函数是如何解析的。其中三个关键字被分类为Section:import,on,service

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

第一次执行next_token()出来,case t_text,此时,nargs = 1, args[0] = service

第二次执行next_token()出来,case t_text,此时, nargs =2, args[0] = service, args[1] = servicemanager,

第三次执行next_token()出来,case t_text,此时, nargs = 3, args[0] = service, args[1] = servicemanager, args[2] = /system/bin/servicemanager

第三次执行next_token()出来,case T_NEWLINE,从keywords.h中我们看到service是一个section,于是会执行parse_new_section

static void parse_new_section(struct parse_state *state, int kw,
                       int nargs, char **args)
{
    printf("[ %s %s ]\n", args[0],
           nargs > 1 ? args[1] : "");
    switch(kw) {
    case K_service:
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
    case K_on:
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
    case K_import:
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line = parse_line_no_op;
}

对于service和action,其实就是实例化一个service和action的结构体,添加到service的list和action的list里面去,并且将parse_line函数转换成对应的prase_line_action/service函数;对于import,则是将import的路径,以及import的文件名保存到import的list里面去。

对于service,parse_new_section的主要做了两件事:1、实例化一个service添加到service的list中;2、将prase_line函数赋值成prase_line_service.

于是我们继续解析第二行: nargs =2, args[0] = class, args[1] = core.

对于class这种属于Option分类的,是用来补充service的信息的。

 case K_class:
        if (nargs != 2) {
            parse_error(state, "class option requires a classname\n");
        } else {
            svc->classname = args[1];
        }
        break;

对于onrestart是属于Option分类,后面接的是Command,用来表示重启要做什么动作。

   case K_onrestart:
        nargs--;
        args++;
        //kw是restart
        kw = lookup_keyword(args[0]);
        if (!kw_is(kw, COMMAND)) {
            parse_error(state, "invalid command '%s'\n", args[0]);
            break;
        }
        kw_nargs = kw_nargs(kw);
        if (nargs < kw_nargs) {
            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
                kw_nargs > 2 ? "arguments" : "argument");
            break;
        }

        cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
        cmd->func = kw_func(kw);
        cmd->nargs = nargs;
        memcpy(cmd->args, args, sizeof(char*) * nargs);
        list_add_tail(&svc->onrestart.commands, &cmd->clist);
        break;

service有一个Command的list,专门用来保存onrestart要做的操作。

对于Action和Service类型的Section,其解析的流程都是类似的,解析的过程就是讲init.rc中的配置,映射到c++中的对象。

对于import类型的Section,其解析的流程就是将import文件的路径和文件名,保存到import_list

static void parse_import(struct parse_state *state, int nargs, char **args)
{
    struct listnode *import_list = state->priv;
    struct import *import;
    char conf_file[PATH_MAX];
    int ret;

    if (nargs != 2) {
        ERROR("single argument needed for import\n");
        return;
    }
    //展开属性,对于变量(即通过$引用的,找到它实际的值,替换进去,保存对应的路径)
    ret = expand_props(conf_file, args[1], sizeof(conf_file));
    if (ret) {
        ERROR("error while handling import on line '%d' in '%s'\n",
              state->line, state->filename);
        return;
    }

    import = calloc(1, sizeof(struct import));
    import->filename = strdup(conf_file);
    list_add_tail(import_list, &import->list);
    INFO("found import '%s', adding to import list", import->filename);
}

本文件解析完成之后,再对import的文件进行同样的解析。

parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         int ret;

         INFO("importing '%s'", import->filename);
         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值