一、简述
Android init.rc文件由系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action, Commands,Services, Options. 在init.rc 文件中一条语句通常是占据一行.单词之间是通过空格符来相隔的,如果需要在单词内使用空格,那么得使用转义字符"\", 如果在一行的末尾有一个反斜杠,那么是换行折叠符号,应该和下一行合并成一起来处理,这样做主要是为了避免一行的字符太长,与C语言中的含义是一致的。注释是以#号开头。 Action和 services显式声明了一个语句块,而commands和options属于最近声明的语句块。在第一个语句块之前 的commands和options会被忽略.
二、关键字
token: 计算机语言中的一个单词,就跟英文中的单词差不多一人概念.class: 类属,即可以为多个service指定一个相同的类属,方便操作同时启动或停止.
三、语句解析
1. 动作(Action)
动作表示了一组命令(commands)组成.动作包含一个触发器,决定了何时执行这个动作。当触发器的条件满足时,这个动作会被加入到已被执行的队列尾。如果此动作在队列中已经存在,那么它将不会执行.一个动作所包含的命令将被依次执行。动作的语法如下所示:
Actions组织形式位:
on
动作的使用示例如下:
on init
export PATH /sbin:/system/sbin:/system/bin:/system/xbin
mkdir /system
init表示一个触发条件,这个触发事件发生后,进行设置环境变量和建立目录的操作称为一个“动作”。
2. 服务(services)
服务是指那些需要在系统初始化时就启动或退出时自动重启的程序.
它的语法结构如下所示:
service
往指定的文件写字符串.
6 属性(Properties)四、init.rc 文件解析过程
文件结构
init.rc 基本单位是 section。
section 有三种类型:
1. on
2. service
3. import
解析 init.rc 的过程就是识别一个个 section 的过程。
在 init.c 中的 main() 中去执行一个个命令。init_parse_config_file("/init.rc");
其代码实现如下:
int init_parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0); //read_file()调用open\lseek\read 将init.rc读出来
if (!data) return -1;
parse_config(fn, data); //调用parse_config开始解析
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)) { //next_token()根据从state.ptr开始遍历
case T_EOF: //遍历到文件结尾,然后goto解析import的.rc文件
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)) { //如果这是一个section的第一行
state.parse_line(&state, 0, 0);
parse_new_section(&state, kw, nargs, args);
} else { //如果这不是一个section的第一行
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);
}
}
next_token() 解析完 init.rc 中一行之后,
会返回T_NEWLINE,这时调用 lookup_keyword 函数来找出这一行的关键字, lookup_keyword返回的是一个整型值,对应keyword_info[]数组的下标,keyword_info[]存放的是keyword_info结构体类型的数据,
struct {
const char *name; //关键字的名称
int (*func)(int nargs, char **args); //对应的处理函数
unsigned char nargs; //参数个数
unsigned char flags; //flag标识关键字的类型,包括COMMAND、OPTION、SECTION
} keyword_info
因此keyword_info[]中存放的是所有关键字的信息,每一项对应一个关键字。
根据每一项的flags就可以判断出关键字的类型,如新的一行是SECTION,就调用parse_new_section()来解析这一行,
如新的一行不是一个SECTION的第一行,那么调用state.parseline()来解析(state.parseline所对应的函数会根据section类型的不同而不同),在parse_new_section()中进行动态设置。
三种类型的section: service、on、import,
service对应的state.parseline为parse_line_service,
on对应的state.parseline为parse_line_action,
import section中只有一行所以没有对应的state.parseline。
service 的名字长度不能超过16个字节
service 的名字只能是字母,数字和'-','_'
5.2 系统组
system/core/include/private/android_filesystem_config.h中的android_ids定义了系统中所有进程的gid值
"root" AID_ROOT
"system" AID_SYSTEM
可以在makefile中用宏PRODUCT_DEFAULT_PROPERTY_OVERRIDES定义缺省的property
系统中有PROP_PATH_SYSTEM_BUILD和PROP_PATH_SYSTEM_DEFAULT
system/build.prop;
system/default.prop
system.prop
并且init 进程创建了/dev/socket/property_service socket去监听 设置prop的请求.
执行完on boot以后,就会依次启动core,main,defualt类中的服务
最终init进程通过执行fork创建进程
所有的service都是init进程的子进程,当init进程执行完init.rc以后,就会监听所有service的退出状态,propertery的设置。
init的这个版本包含运行”bootcharting”的代码:生成一个日志文件,后期能够被 www.bootchart.org
提供的工具处理。
在虚拟机中,使用-bootchart 选项来使启动的时候带有bootcharting持续秒。
在一个设备中,使用命令创建 /data/bootchart/start:
adb shell 'echo $TIMEOUT > /data/bootchart/start'
$TIMEOUT的值对应着期望bootchart持续的秒数。当这些时间过后,Bootcharting将会停止。
你可以通过下面的命令在任何时候停bootcharting:
adb shell 'echo 1 > /data/bootchart/stop'
注意,/data/bootchart/stop会在bootcharting最后被init自动删除。对于/data/bootchart/start
情况并非如此,所以当你收集完数据之后,不要忘记删除他。
日志文件被写入/data/bootchart中。一个脚本被提供去恢复他们,并且创建一个bootchart.tgz文件
,这个文件可以被bootchart命令行工具使用:
sudo apt-get install pybootchartgui
# grab-bootchart.sh uses $ANDROID_SERIAL.
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
一个需要注意的事情就是,bootchart将会显示init好像是他从0s的时候开始运行。当内核
开始init的时候,你必须查看dmesg的工作。
默认的,由init执行的程序将会把标准输出和标准错误丢入到/dev/null。为了帮助调试,
你可以通过安卓程序日志封装程序运行你的程序。这个将会重定向标准输出/标准错误到
安卓日志系统中。
例如 service akmd /system/bin/logwrapper /sbin/akmd
当在init中自己运行的时候,为了快速的转变,使用:
mm -j
m ramdisk-nodeps
m bootimage-nodeps
adb reboot bootloader
fastboot boot $ANDROID_PRODUCT_OUT/boot.img
可选的,使用虚拟机:
emulator -partition-size 1024 -verbose -show-kernel -no-window
在klog_init()调用之后,你可能想要调用klog_set_level(6),所以你需要在
dmesg或者是虚拟机输出中查看内核日志。