首先,个人为了很好的总结这方面的知识体系,以下绝大多数内容都是博主从网上那些大神多方面摘抄过来的。
一、文件简介
init.rc:Android在启动过程中读取的启动脚本文件,主要完成一些初级的初始化,在/system/core/init/init.c中解析。rc 经常被用作程序之启动脚本的文件名。它是“run commands”(运行命令)的缩写。
init.xx.rc:与具体CPU相关的启动脚本,比如对于MTK的CPU,名字为init.usb.rc,init.trace.rc。在init.rc之后得到解析。
对于运行的设备,两个文件都位于根目录下:autochips\device\atc\ac8317\configs
对于编译后的源码,位于out\target\product\ac8317\root
二、init.rc和init.xx.rc文件的修改
我们以 init.rc 来入手,学习 rc 的用法。
\autochips\device\atc\ac8317\configs\init_for_mmc.rc
system/core/init.c
文件结构
init.rc 基本单位是 section。
section 有三种类型:
1. on
2. service
3. import
如果想对这方面作个更加详细的理解的朋友请移步至Android init.rc文件浅析 阅读
on 类型
on 类型 表示一系列的命令组合,eg:
on init
mkdir /productinfo 0771 system system
# SPRD: modify for storage manage @{
mount tmpfs tmpfs /storage mode=0751,gid=1028
# @}
mkdir /mnt/media_rw/sdcard0 0700 media_rw media_rw
mkdir /storage/sdcard0 0700 root root
mkdir /mnt/shell/emulated 0700 shell shell
mkdir /storage/emulated 0555 root root
# SPRD: move this to board level init @{
#export EXTERNAL_STORAGE /storage/emulated/legacy
#export SECONDARY_STORAGE /storage/sdcard0
# @}
# SPRD: for storage manage @{
export LEGACY_STORAGE /storage/emulated/legacy
# @}
这样一个 section 里面包含了多个命令。命令的执行是以 section 为单位的。
上面这些命令都会一起顺序执行,不会单独执行。
他们的执行由 init.c 的 main() 决定。在当其中调用
init.c文件:
action_for_each_trigger("early-init", action_add_queue_tail);
时,就将 on init 开始的这样一个 section 里所有的命令加入到一个执行队列,在未来某个时候会顺序执行队列里的命令。
该方法在init_parser.c文件中实现:
void action_for_each_trigger(const char *trigger,
void (*func)(struct action *act))
{
struct listnode *node;
struct action *act;
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
if (!strcmp(act->name, trigger)) {
func(act);
}
}
}
service 类型
service 类型 的section 表示一个可执行的程序,下面是个人工作内容中片段代码:
例一:
service gocsdk /system/bin/gocsdk
class main
disabled
oneshot
on property:sys.boot_completed=1
chmod 777 /dev/goc_serial
start gocsdk
gocsdk 作为一个名字标识了这个 service,这个可执行程序的位置在 /system/bin/gocsdk。
下面的 disable、oneshot 被称为 options,options 是用来描述的 service 的特点,不同的 service 有不同的 options。
service 的执行时间是在property:sys.boot_completed=1 这个命令被执行的时候,这个命令总存在于某个 on 类型的section 中作为一个服务启动的动作(Action)。
就拿上面的代码来说property:sys.boot_completed=1满足条件被执行时,则会启动这个类型为 main的 gocsdk service。
例二:
service yo_service1 /system/bin/yo_service1
class core
user system
disabled
group system radio shell
oneshot
on yo_fs
class_start core
其中 yo_service1 这个 service 的类型是 core。
在 yo_fs 被调用的时候则将会 class_start 而执行所有 类型为 core 的 service。
import 类型
import 类型表示包含了另外一些 section,在解析完 init.rc 后会继续调用 init_parse_config_file 来解析引入的 .rc 文件。
eg:
比如我们在 init.sc8830.rc 的开始可以看到
import /init.usb.rc
import /init.trace.rc
表示在运行完本 rc 后还将继续运行 init.usb.rc 和 init.trace.rc。
init.rc 文件解析过程
解析 init.rc 的过程就是识别一个个 section 的过程。
在 init.c 中的 main() 中去执行一个个命令。
(android采用双向链表来存储section的信息,解析完成之后,会得到三个双向链表action_list、service_list、import_list来分别存储三种section的信息上。)
system/core/init/init.c
在 init.c 中调用
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;
}
parse_config() 代码:
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。