在 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;则触发一次