根据上面的代码可知,在init中会解析两个配置文件,其中一个是系统配置文件init.rc,另外一个是和硬件平台相关的配置文件。以HTC G7手机为例,这个配置文件名为init.bravo.rc,其中bravo是硬件平台的名称。对这两个配置文件进行解析,调用的是同一个parse_config_file函数。下面就来看这个函数,在分析过程中以init.rc为主。
**parser.c**
~~~
int parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);//读取配置文件的内容,这个文件是init.rc。
if (!data) return -1;
parse_config(fn,data); //调用parse_config做真正的解析
return 0;
}
~~~
读取完文件的内容后,将调用parse_config进行解析,这个函数的代码如下所示:
**parser.c**
~~~
static void parse_config(const char *fn, char*s)
{
struct parse_state state;
char *args[SVC_MAXARGS];
int nargs;
nargs = 0;
state.filename = fn;
state.line = 1;
state.ptr = s;
state.nexttoken = 0;
state.parse_line = parse_line_no_op; //设置解析函数,不同的内容用不同的解析函数
for (;;) {
switch(next_token(&state)) {
case T_EOF:
state.parse_line(&state, 0, 0);
return;
caseT_NEWLINE:
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);//解析这个SECTION。
} else {
state.parse_line(&state, nargs, args);
}
nargs = 0;
}
break;
case T_TEXT:
......
break;
}
}
}
~~~
上面就是parse_config函数,代码虽短,实际却比较复杂。从整体来说,parse_config首先会找到配置文件的一个section,然后针对不同的 section使用不同的解析函数来解析。那么,什么是section呢?这和init.rc文件的组织结构有关。先不必急着去看init.rc,还是先到代码中去寻找答案。
1. 关键字定义
keywords.h这个文件定义了init中使用的关键字,它的用法很有意思,先来看这个文件,代码如下所示:
**keywords.h**
~~~
#ifndef KEYWORD //如果没有定义KEYWORD宏,则走下面的分支
......//声明一些函数,这些函数就是前面所说Action的执行函数。
int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
......
int do_restart(int nargs, char **args);
......
#define __MAKE_KEYWORD_ENUM__ //定义一个宏
/*
定义KEYWORD宏,虽然有四个参数,不过这里只用第一个,其中K_##symbol中的##表示连接
的意思,即最后得到的值为K_symbol。symbol其实就是init.rc中的关键字
*/
#define KEYWORD(symbol, flags, nargs, func)K_##symbol,
enum { //定义一个枚举,这个枚举定义了各个关键字的枚举值。
K_UNKNOWN,
#endif
......
//根据上面KEYWORD的定义,这里将得到一个枚举值K_class,
KEYWORD(class, OPTION, 0, 0)
KEYWORD(class_start, COMMAND, 1, do_class_start)//K_class_start,
KEYWORD(class_stop, COMMAND, 1, do_class_stop)//K_class_stop,
KEYWORD(on, SECTION, 0, 0)//K_on,
KEYWORD(oneshot, OPTION, 0, 0)
KEYWORD(onrestart, OPTION, 0, 0)
KEYWORD(restart, COMMAND, 1,do_restart)
KEYWORD(service, SECTION, 0,0)
......
KEYWORD(socket, OPTION, 0, 0)
KEYWORD(start, COMMAND, 1,do_start)
KEYWORD(stop, COMMAND, 1,do_stop)
......
#ifdef __MAKE_KEYWORD_ENUM__
KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD //取消KEYWORD宏定义
#endif
~~~
keywords.h好像没什么奇特,不过是个简单的头文件。为什么说它的用法很有意思呢?来看代码中是如何使用它的,如下所示:
**parser.c**
~~~
......//parser.c中将包含keywords.h头文件,而且还不只一次!!
//第一次包含keywords.h,根据keywords.h的代码,我们首先会得到一个枚举定义
#include "keywords.h"
/*
重新定义KEYWORD宏,这回四个参数全用上了,看起来好像是一个结构体。其中#symbol表示
一个字符串,其值为“symbol”。
*/
#define KEYWORD(symbol, flags, nargs, func) \
[K_##symbol ] = { #symbol, func, nargs + 1, flags, },
//定义一个结构体keyword_info数组,它用来描述关键字的一些属性,请注意里面的注释内容。
struct {
constchar *name; //关键字的名。
int(*func)(int nargs, char **args);//对应关键字的处理函数。
unsignedchar nargs;//参数个数,每个关键字的参数个数是固定的。
//关键字的属性,有三种属性,COMMAND、OPTION和SECTION。其中COMMAND有对应的处理函数
unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
[ K_UNKNOWN ] = { "unknown", 0, 0, 0},
/*
第二次包含keywords.h,由于已经重新定了KEYWORD宏,所以以前那些作为枚举值的关键字
现在变成keyword_info数组的索引了。
*/
#include "keywords.h"
};
#undef KEYWORD
//一些辅助宏,帮助我们快速操作keyword_info中的内容。
#define kw_is(kw, type) (keyword_info[kw].flags& (type))
#define kw_name(kw) (keyword_info[kw].name)
#define kw_func(kw) (keyword_info[kw].func)
#define kw_nargs(kw) (keyword_info[kw].nargs)
~~~
现在领略了keywords.h的神奇之处了吧?原来它干了两件事情:
- 第一次包含keyworks.h时,它声明了一些诸如do_classstart这样的函数,另外还定义了一个枚举,枚举值为K_class,K_mkdir等关键字。
- 第二次包含keywords.h后,得到了一个keyword_info结构体数组,这个keyword_info结构体数组以前面定义的枚举值为索引,存储对应的关键字信息,这些信息包括关键字名、处理函数、处理函数的参数个数,以及属性。
目前,关键字信息中最重要的就是symbol和flags了。什么样的关键字被认为是section呢?根据keywords.h的定义,symbol为下面两个的关键字表示section:
~~~
KEYWORD(on, SECTION, 0, 0)
KEYWORD(service, SECTION, 0, 0)
~~~
有了上面的知识,再来看配置文件init.rc的内容。
2. init.rc的解析
init.rc的内容如下所示:(我们截取了部分内容,注意,其中的注释符号是#。)
**init.rc**
~~~
on init #根据上面的分析,on关键字标示一个section,对应的名字是”init”
...... #下面所有的内容都属于这个section,直到下一个section开始时。
exportPATH /sbin:/system/sbin:/system/bin:/system/xbin
exportLD_LIBRARY_PATH /system/lib
exportANDROID_BOOTLOGO 1 #根据keywords.h的定义,export表示一个COMMAND
export ANDROID_ROOT /system
exportANDROID_ASSETS /system/app
...... #省略部分内容
on boot #这是一个新的section,名为”boot”
ifup lo#这是一个COMMAND
hostname localhost
domainname localdomain
......
#class_start也是一个COMMAND,对应函数为do_class_start,很重要,切记。
class_startdefault
......
#下面这个section的意思是:待属性persist.service.adb.enable的值变为1后,
#需要执行对应的COMMAND,这个COMMAND是start adbd
onproperty:persist.service.adb.enable=1
start adbd //start是一个COMMAND
on property:persist.service.adb.enable=0
stopadbd
......
#service也是section的标示,对应section的名为“zygote“
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \
--start-system-server
socketzygote stream 666 #socket关键字表示OPTION
onrestart write /sys/android_power/request_state wake #onrestart也是OPTION
onrestart write /sys/power/state on
onrestart restart media
#一个section,名为”media”
service media /system/bin/mediaserver
usermedia
groupsystem audio camera graphics inet net_bt net_bt_admin net_raw
iopriort 4
~~~
从上面对init.rc的分析中可知:
- 一个section的内容从这个标示section的关键字开始,到下一个标示section的地方结束。
- init.rc中出现了名为boot和init的section,这里的boot和init,就是前面介绍的动作执行四个阶段中的boot和init。也就是说,在boot阶段执行的动作都是由boot这个section定义的。
另外还可发现,zygote被放在了一个servicesection中。下面以zygote这个section为例,介绍service是如何解析的。
- 前言
- 第1章 阅读前的准备工作
- 1.1 系统架构
- 1.1.1 Android系统架构
- 1.1.2 本书的架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.3 工具介绍
- 1.3.1 Source Insight介绍
- 1.3.2 Busybox的使用
- 1.4 本章小结
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 学习JNI的实例:MediaScanner
- 2.3 Java层的MediaScanner分析
- 2.3.1 加载JNI库
- 2.3.2 Java的native函数和总结
- 2.4 JNI层MediaScanner的分析
- 2.4.1 注册JNI函数
- 2.4.2 数据类型转换
- 2.4.3 JNIEnv介绍
- 2.4.4 通过JNIEnv操作jobject
- 2.4.5 jstring介绍
- 2.4.6 JNI类型签名介绍
- 2.4.7 垃圾回收
- 2.4.8 JNI中的异常处理
- 2.5 本章小结
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 属性服务
- 3.3 本章小结
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 关于zygote的总结
- 4.3 SystemServer分析
- 4.3.1 SystemServer的诞生
- 4.3.2 SystemServer的重要使命
- 4.3.3 关于 SystemServer的总结
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService发送请求
- 4.4.2 有求必应之响应请求
- 4.4.3 关于zygote分裂的总结
- 4.5 拓展思考
- 4.5.1 虚拟机heapsize的限制
- 4.5.2 开机速度优化
- 4.5.3 Watchdog分析
- 4.6 本章小结
- 第5章 深入理解常见类
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初识影子对象
- 5.2.2 第二板斧--由弱生强
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 轻量级的引用计数控制类LightRefBase
- 5.2.5 题外话-三板斧的来历
- 5.3 Thread类及常用同步类分析
- 5.3.1 一个变量引发的思考
- 5.3.2 常用同步类
- 5.4 Looper和Handler类分析
- 5.4.1 Looper类分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步关系
- 5.4.4 HandlerThread介绍
- 5.5 本章小结
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函数
- 6.2.2 独一无二的ProcessState
- 6.2.3 时空穿越魔术-defaultServiceManager
- 6.2.4 注册MediaPlayerService
- 6.2.5 秋风扫落叶-StartThread Pool和join Thread Pool分析
- 6.2.6 你彻底明白了吗
- 6.3 服务总管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服务的注册
- 6.3.3 ServiceManager存在的意义
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查询ServiceManager
- 6.4.2 子承父业
- 6.5 拓展思考
- 6.5.1 Binder和线程的关系
- 6.5.2 有人情味的讣告
- 6.5.3 匿名Service
- 6.6 学以致用
- 6.6.1 纯Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小结
- 第7章 深入理解Audio系统
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介绍
- 7.2.2 AudioTrack(Java空间)分析
- 7.2.3 AudioTrack(Native空间)分析
- 7.2.4 关于AudioTrack的总结
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的诞生
- 7.3.2 通过流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 关于AudioFlinger的总结
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的创建
- 7.4.2 重回AudioTrack
- 7.4.3 声音路由切换实例分析
- 7.4.4 关于AudioPolicy的总结
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 题外话
- 7.6 本章小结
- 第8章 深入理解Surface系统
- 8.1 概述
- 8.2 一个Activity的显示
- 8.2.1 Activity的创建
- 8.2.2 Activity的UI绘制
- 8.2.3 关于Activity的总结
- 8.3 初识Surface
- 8.3.1 和Surface有关的流程总结
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI层分析
- 8.3.4 Surface和画图
- 8.3.5 初识Surface小结
- 8.4 深入分析Surface
- 8.4.1 与Surface相关的基础知识介绍
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface对象的创建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介绍
- 8.4.7 深入分析Surface的总结
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的诞生
- 8.5.2 SF工作线程分析
- 8.5.3 Transaction分析
- 8.5.4 关于SurfaceFlinger的总结
- 8.6 拓展思考
- 8.6.1 Surface系统的CB对象分析
- 8.6.2 ViewRoot的你问我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小结
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理与机制分析
- 9.2.1 Netlink和Uevent介绍
- 9.2.2 初识Vold
- 9.2.3 NetlinkManager模块分析
- 9.2.4 VolumeManager模块分析
- 9.2.5 CommandListener模块分析
- 9.2.6 Vold实例分析
- 9.2.7 关于Vold的总结
- 9.3 Rild的原理与机制分析
- 9.3.1 初识Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 关于Rild main函数的总结
- 9.3.6 Rild实例分析
- 9.3.7 关于Rild的总结
- 9.4 拓展思考
- 9.4.1 嵌入式系统的存储知识介绍
- 9.4.2 Rild和Phone的改进探讨
- 9.5 本章小结
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模块分析
- 10.2.2 MSS模块分析
- 10.2.3 android.process.media媒体扫描工作的流程总结
- 10.3 MediaScanner分析
- 10.3.1 Java层分析
- 10.3.2 JNI层分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 关于MediaScanner的总结
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介绍
- 10.4.2 我问你答
- 10.5 本章小结