在 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 <= 0) //超时返回的话直接从头运行
{ continue; }
//如果有数据,则处理数据
for (i = 0; i < fd_count; i++)
{
if (ufds[i].revents & POLLIN)
{
if (ufds[i].fd == get_property_set_fd())
{ handle_property_set_fd(); }//有属性写入
else if (ufds[i].fd == get_keychord_fd())
{ handle_keychord(); }
else if (ufds[i].fd == get_signal_fd())
{ handle_signal(); }
}
}
}
return 0;
}
.rc 文件中关于类似 on property:sys.init_log_level=* 的解析过程:
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++;
if (nargs) {
int kw = 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,那就是command
state.parse_line(&state, nargs, args);
}
nargs = 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 = 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;
}
.rc 文件中有三种 section
1:on 开头的
on property:sys.init_log_level=*
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
这里,我们只关心 on property:sys.init_log_level=* 这种
static void *parse_action(struct parse_state *state, int nargs, char **args)
{
struct action *act;
//解析 section 构造一个 action 挂入 action_list
act = calloc(1, sizeof(*act));
act->name = 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 = state->context;
int (*func)(int nargs, char **args);
int kw, n;
//解析这个 section 对应的 commond 挂入 action 的 commands 链表
cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
cmd->func = kw_func(kw);
cmd->line = state->line;
cmd->filename = state->filename;
cmd->nargs = nargs;
memcpy(cmd->args, args, sizeof(char*) * nargs);
list_add_tail(&act->commands, &cmd->clist);
}
也就是说,在解析完 .rc 文件以后,action_list 链表中填充了大量的 action ,名字类似于property:sys.init_log_level=*,并且可以找到它们对应的 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 文件一样,构造 action command 放入 action_list
act = calloc(1, sizeof(*act));
act->name = name;
list_init(&act->commands);
list_init(&act->qlist);
cmd = calloc(1, sizeof(*cmd));
cmd->func = func;
cmd->args[0] = name;
cmd->nargs = 1;
list_add_tail(&act->commands, &cmd->clist);
list_add_tail(&action_list, &act->alist);
action_add_queue_tail(act);
}
值得注意的是,这里还调用了 action_add_queue_tail(act) 将该 action 挂入了 action_queue 链表,在main函数最后的for循环中,将取出 action_queue 链表中的 action ,调用它们的 command ,后面在分析
这里构造了名字为property_service_init、queue_property_triggers的两个 action ,分别都放入action_list\action_queue链表
来看陷入for循环之后的工作:
for (;;)
{
int nr, i, timeout = -1;
//重点稍后分析
execute_one_command();
restart_processes();
//使用poll轮训是否有进程设置属性
...
nr = poll(ufds, fd_count, timeout);
if (nr <= 0) //超时返回的话直接从头运行
{ continue; }
//如果有数据,则处理数据
for (i = 0; i < fd_count; i++)
{
if (ufds[i].revents & POLLIN)
{
if (ufds[i].fd == get_property_set_fd())
{ handle_property_set_fd(); }//有属性写入
else if (ufds[i].fd == get_keychord_fd())
{ handle_keychord(); }
else if (ufds[i].fd == 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 = action_remove_queue_head(); //从 action_queue 链表中取出 action
cur_command = NULL;
}
ret = cur_command->func(cur_command->nargs, cur_command->args);//执行 action 的 command
}
现在来分析一下前面的:
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 = 1;
return 0;
}
void queue_all_property_triggers()
{
struct listnode *node;
struct action *act;
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
if (!strncmp(act->name, "property:", strlen("property:"))) {
/* parse property name and value
syntax is property:<name>=<value> */
const char* name = act->name + strlen("property:");
const char* equals = strchr(name, '=');
if (equals) {
char prop_name[PROP_NAME_MAX + 1];
char value[PROP_VALUE_MAX];
int length = 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] = 0;
/* 如果对应的属性存在,判断是否等于设置的值,或者是* */
ret = property_get(prop_name, value);
if (ret > 0 && (!strcmp(equals + 1, value) ||
!strcmp(equals + 1, "*"))) {
//如果判断成功,挂入 action_queue
action_add_queue_tail(act);
}
}
}
}
}
}
举例分析:property:sys.init_log_level=*
name sys.init_log_level
value *
如果系统中存在 sys.init_log_level 这个属性,就将这个 action 挂入 action_queue
那么,在下次调用 execute_one_command 的时候,就会调用 sys.init_log_level 对应的 command loglevel ${sys.init_log_level} 设置打印级别
也就验证了实验结论,在系统启动过程中,如果存在满足条件的属性,则触发一次!
下次 execute_one_command 什么时候调用?很简单,poll超时返回时,就回到for的起点,调用了!
poll 有数据时: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 = node_to_item(node, struct action, alist);
if (!strncmp(act->name, "property:", strlen("property:"))) {
const char *test = act->name + strlen("property:");
int name_length = strlen(name);
if (!strncmp(name, test, name_length) &&
test[name_length] == '=' &&
(!strcmp(test + name_length + 1, value) ||
!strcmp(test + name_length + 1, "*"))) {
action_add_queue_tail(act);
}
}
}
}
拿新设置的属性的名字和action_list中存在的每一个action的名字做比较(含有property:的action),如果值命中了那么放入 action_queue 链表
在execute_one_command 中就会调用 commmand 了
验证了实验结论中的:系统运行中如果属性发生变化,且新值等于设定值,则触发一次
Android init.rc on property相关推荐
- Android init.rc文件解析过程详解(三)
Android init.rc文件解析过程详解(三) 三.相关结构体 1.listnode listnode结构体用于建立双向链表,这种结构广泛用于kernel代码中, android源代码中定义了l ...
- Android init.rc 服务启动不成功
Android init.rc 服务启动不成功 问题 在开发过程中发现一个问题,我们需要在开机的时候判断硬件版本号去启动服务, 服务的名字是ledservice和ledservice4,但是发现每次烧 ...
- 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)
Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...
- Android init.rc文件格式解析
/****************************************************************************** Android init.rc文件格式解 ...
- Android init.rc文件解析过程详解(二)
Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...
- Android init.rc文件解析过程详解(一)
Android init.rc文件解析过程详解(一) 一.init.rc文件结构介绍 init.rc文件基本组成单位是section, section分为三种类型,分别由三个关键字(所谓关键字 ...
- android init.rc中启动的service 默认是disable的,后续如何启动此服务
如果 android init.rc中启动的service 默认是disable的,如何才能启动此服务呢? init.rc中可以直接启动service 附带的参数决定启动程序的状态,例如数据业务中配置 ...
- Android 系统(242)---Android init.rc执行顺序
Android init.rc执行顺序 1. 所有的action运行于service之前 2. 下面为各个section的执行顺序,英文编号的section是系统内建的(写死在init.c中的命令) ...
- Android init.rc如何启动service去执行sh脚本
在Android开发中经常会遇到,在应用层想去执行一个脚本来完成某些底层相关的操作,但在应用层又没有root权限. 所以,老大给出一种方法完美解决此问题,又把上层和底层进行了隔离,非常好的策略. 1. ...
- android init.rc语法标准 .
Android 初始化语言由四大类声明组成: 行为类(Actions), 命令类(Commands) ,服务类(Services), 选项类(Options). * 初始化语言以行为单位,由以空格间隔 ...
最新文章
- 大数据平台安全标准设计
- VMware workstation 14安装windows虚拟机
- Spring框架第一天知识总结
- 百度关键词抓取工具_VBA利用XMLHTTP抓取百度查询关键词结果的个数
- vue使用better-scroll实现下拉刷新、上拉加载
- 修改ubuntu的IP地址,静态IP地址
- 应用软件更新提醒单页HTML网站源码
- python爬取appstore的评论数据的步骤_python数据抓取分析
- 浅谈分布式一致性协议之3PC
- networkxpdf_1 NetworkX概述
- Fragment运行时错误
- java isreachable_Java网络编程从入门到精通(12):使用isReachable方法探测主机是否可以连通...
- 手机性能测试指标及操作
- mysql实验五索引和数据完整性_实验六 索引和数据完整性约束
- python基础 // 与 / % 的区别
- 单例模式(Python中的单例类)
- 汽车温度采集记录测量管理的重要性以及K-TC测量模块介绍 热管理
- postgresql 表文件介绍
- 开源的NAS软件项目存储
- 文献笔记|自我管理相关 self- regulation
热门文章
- 机器学习?有无监督、弱监督、半监督、强化、多示例学习是什么
- PHP在线网课问答题库搜索,推荐一个大学mooc网课答案题库在线查询公众号
- C++ 算术平均数及几何平均数
- 计算机桌面计算机快捷方式不见,桌面快捷方式不见了,教您桌面快捷方式不见了怎么办...
- 制作风格——百变幻灯片,完全DIY(高级教程)
- 电阻转换温度值c语言,PT1000电阻值转化为温度值的计算公式
- 工作流引擎的流程业务表设计
- 互联网公司干不好上门维修?
- css中button宽高大小不包含boder问题和文字不居中问题
- 【CVE】CVE-2015-5254:ActiveMQ 反序列化漏洞利用