热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Androidinit.rconproperty

在init.rc中,可以见到下面类似的用法,当一个属性值等于XX时,触发下面的事件,比如启动一个进程,或者设置

在 init.rc 中,可以见到下面类似的用法,当一个属性值等于XX时,触发下面的事件,比如启动一个进程,或者设置打印级别

on property:sys.init_log_level=*

    loglevel ${sys.init_log_level}




那么它是如何实现的,启动时触发一次?还是任何时刻只要属性值满足条件就触发? 

实验验证结果:

1、启动时,如果属性满足设定条件会触发一次

2、系统运行中如果属性发生变化,且新值等于设定值,则触发一次




代码分析:

init.c (system\core\init)

int main(int argc, char **argv)
{
    ...
    //分配存放属性的共享内存
    property_init();
    ...
    INFO("property init\n");
    property_load_boot_defaults(); //加载默认的属性
    INFO("reading config file\n");
    init_parse_config_file("/init.rc");//解析init.rc文件
    ...
    //处理 on early-init\init section部分,本文暂不关心
    action_for_each_trigger("early-init", action_add_queue_tail);
    action_for_each_trigger("init", action_add_queue_tail);
    //这个是重点
    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
    //陷入死循环,等待接收别的进程设置属性
    for (;;)
    {
        int nr, i, timeout = -1;
        //重点稍后分析
        execute_one_command();
        restart_processes();
        //使用poll轮训是否有进程设置属性
        ...

        nr = poll(ufds, fd_count, timeout);
        if (nr <&#61; 0)  //超时返回的话直接从头运行
            { continue; }
        //如果有数据&#xff0c;则处理数据
        for (i &#61; 0; i
        {
            if (ufds[i].revents & POLLIN)
            {
                if (ufds[i].fd &#61;&#61; get_property_set_fd())
                    { handle_property_set_fd(); }//有属性写入
                else if (ufds[i].fd &#61;&#61; get_keychord_fd())
                    { handle_keychord(); }
                else if (ufds[i].fd &#61;&#61; get_signal_fd())
                    { handle_signal(); }
            }
        }
    }

    return 0;
}



.rc 文件中关于类似 on property:sys.init_log_level&#61;* 的解析过程&#xff1a;

int init_parse_config_file(const char *fn)
    parse_config(fn, data);
    return 0;
}


static void parse_config(const char *fn, char *s)
{
    ...
    for (;;) {
        switch (next_token(&state)) {
        ...
        case T_NEWLINE:             //如果是新的一行
            state.line&#43;&#43;;
            if (nargs) {
                int kw &#61; lookup_keyword(args[0]);
                if (kw_is(kw, SECTION)) {    //判断是否是section
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {                     //不是section&#xff0c;那就是command
                    state.parse_line(&state, nargs, args);
                }
                nargs &#61; 0;
            }
            break;
        ...
    }

parser_done:
    ...
}


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 &#61; parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line &#61; parse_line_service;
            return;
        }
        break;
    case K_on:
        state->context &#61; parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line &#61; parse_line_action;
            return;
        }
        break;
    case K_import:
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line &#61; parse_line_no_op;
}


.rc 文件中有三种 section

1:on 开头的 



on property:sys.init_log_level&#61;*

    loglevel ${sys.init_log_level}

2:service 开头的 

    service netd /system/bin/netd

3:import开头的

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc


这里&#xff0c;我们只关心 on property:sys.init_log_level&#61;* 这种

static void *parse_action(struct parse_state *state, int nargs, char **args)
{
    struct action *act;
    //解析 section 构造一个 action 挂入 action_list
    act &#61; calloc(1, sizeof(*act));
    act->name &#61; args[1];
    list_init(&act->commands);
    list_init(&act->qlist);
    list_add_tail(&action_list, &act->alist);
        /* XXX add to hash */
    return act;
}


static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
    struct command *cmd;
    struct action *act &#61; state->context;
    int (*func)(int nargs, char **args);
    int kw, n;
    //解析这个 section 对应的 commond 挂入 action 的 commands 链表
    cmd &#61; malloc(sizeof(*cmd) &#43; sizeof(char*) * nargs);
    cmd->func &#61; kw_func(kw);
    cmd->line &#61; state->line;
    cmd->filename &#61; state->filename;
    cmd->nargs &#61; nargs;
    memcpy(cmd->args, args, sizeof(char*) * nargs);
    list_add_tail(&act->commands, &cmd->clist);
}
也就是说&#xff0c;在解析完 .rc 文件以后&#xff0c;action_list 链表中填充了大量的 action &#xff0c;名字类似于property:sys.init_log_level&#61;*&#xff0c;并且可以找到它们对应的 command .





queue_builtin_action(property_service_init_action, "property_service_init");

queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
    struct action *act;
    struct command *cmd;
    //和解析 .rc 文件一样&#xff0c;构造 action command 放入 action_list
    act &#61; calloc(1, sizeof(*act));
    act->name &#61; name;
    list_init(&act->commands);
    list_init(&act->qlist);

    cmd &#61; calloc(1, sizeof(*cmd));
    cmd->func &#61; func;
    cmd->args[0] &#61; name;
    cmd->nargs &#61; 1;
    list_add_tail(&act->commands, &cmd->clist);

    list_add_tail(&action_list, &act->alist);
    action_add_queue_tail(act);
}


值得注意的是&#xff0c;这里还调用了 action_add_queue_tail(act) 将该 action 挂入了 action_queue 链表&#xff0c;在main函数最后的for循环中&#xff0c;将取出 action_queue 链表中的 action ,调用它们的 command &#xff0c;后面在分析

这里构造了名字为property_service_init、queue_property_triggers的两个 action ,分别都放入action_list\action_queue链表


来看陷入for循环之后的工作&#xff1a;

for (;;)
    {
        int nr, i, timeout &#61; -1;
        //重点稍后分析
        execute_one_command();
        restart_processes();
               //使用poll轮训是否有进程设置属性
        ...

        nr &#61; poll(ufds, fd_count, timeout);
        if (nr <&#61; 0)  //超时返回的话直接从头运行
            { continue; }
               //如果有数据&#xff0c;则处理数据
        for (i &#61; 0; i
        {
            if (ufds[i].revents & POLLIN)
            {
                if (ufds[i].fd &#61;&#61; get_property_set_fd())
                    { handle_property_set_fd(); }//有属性写入
                else if (ufds[i].fd &#61;&#61; get_keychord_fd())
                    { handle_keychord(); }
                else if (ufds[i].fd &#61;&#61; get_signal_fd())
                    { handle_signal(); }
            }
        }
    }
1 execute_one_command();
void execute_one_command(void)
{
    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
        cur_action &#61; action_remove_queue_head(); //从 action_queue 链表中取出 action 
        cur_command &#61; NULL;
    } 
    ret &#61; cur_command->func(cur_command->nargs, cur_command->args);//执行 action 的 command
}
现在来分析一下前面的&#xff1a;
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
这里就会调用 property_service_init_action 、queue_property_triggers_action
static int property_service_init_action(int nargs, char **args)
{
    start_property_service(); //创建用于获取属性的 socket,不是关心的重点
    return 0;
}
static int queue_property_triggers_action(int nargs, char **args)
{
    queue_all_property_triggers();
    property_triggers_enabled &#61; 1;
    return 0;
}
void queue_all_property_triggers()
{
    struct listnode *node;
    struct action *act;
    list_for_each(node, &action_list) {
        act &#61; node_to_item(node, struct action, alist);
        if (!strncmp(act->name, "property:", strlen("property:"))) {
            /* parse property name and value
               syntax is property:&#61; */
            const char* name &#61; act->name &#43; strlen("property:");
            const char* equals &#61; strchr(name, &#39;&#61;&#39;);
            if (equals) {
                char prop_name[PROP_NAME_MAX &#43; 1];
                char value[PROP_VALUE_MAX];
                int length &#61; equals - name;
                if (length > PROP_NAME_MAX) {
                    ERROR("property name too long in trigger %s", act->name);
                } else {
                    int ret;
                    memcpy(prop_name, name, length);
                    prop_name[length] &#61; 0;

                    /* 如果对应的属性存在&#xff0c;判断是否等于设置的值&#xff0c;或者是* */
                    ret &#61; property_get(prop_name, value);
                    if (ret > 0 && (!strcmp(equals &#43; 1, value) ||
                                    !strcmp(equals &#43; 1, "*"))) {
                        //如果判断成功&#xff0c;挂入 action_queue
                        action_add_queue_tail(act);
                    }
                }
            }
        }
    }
}
举例分析&#xff1a;property:sys.init_log_level&#61;*
name sys.init_log_level
value *
如果系统中存在 sys.init_log_level 这个属性&#xff0c;就将这个 action 挂入 action_queue
那么&#xff0c;在下次调用 execute_one_command 的时候&#xff0c;就会调用 sys.init_log_level 对应的 command loglevel ${sys.init_log_level} 设置打印级别
也就验证了实验结论&#xff0c;在系统启动过程中&#xff0c;如果存在满足条件的属性&#xff0c;则触发一次&#xff01;
下次 execute_one_command 什么时候调用&#xff1f;很简单&#xff0c;poll超时返回时&#xff0c;就回到for的起点&#xff0c;调用了&#xff01;

poll 有数据时&#xff1a;handle_property_set_fd

handle_property_set_fd

    property_set((char*) msg.name, (char*) msg.value);

        //设置属性

        property_changed(name, value);

void property_changed(const char *name, const char *value)
{
    if (property_triggers_enabled) //前面 queue_property_triggers_action 函数中已经设置为1
        queue_property_triggers(name, value);
}
void queue_property_triggers(const char *name, const char *value)
{
    struct listnode *node;
    struct action *act;
    list_for_each(node, &action_list) {
        act &#61; node_to_item(node, struct action, alist);
        if (!strncmp(act->name, "property:", strlen("property:"))) {
            const char *test &#61; act->name &#43; strlen("property:");
            int name_length &#61; strlen(name);

            if (!strncmp(name, test, name_length) &&
                    test[name_length] &#61;&#61; &#39;&#61;&#39; &&
                    (!strcmp(test &#43; name_length &#43; 1, value) ||
                     !strcmp(test &#43; name_length &#43; 1, "*"))) {
                action_add_queue_tail(act);
            }
        }
    }
}
拿新设置的属性的名字和action_list中存在的每一个action的名字做比较&#xff08;含有property:的action&#xff09;,如果值命中了那么放入 action_queue 链表

在execute_one_command 中就会调用 commmand 了

验证了实验结论中的&#xff1a;系统运行中如果属性发生变化&#xff0c;且新值等于设定值&#xff0c;则触发一次




推荐阅读
author-avatar
POWER_WALKING_823
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有