这篇系统启动流程分析借鉴了不少网络上的资源,如下
写这篇文章的目的是在阅读上面师傅的产出并且对照手上的Android7.0版本的时候,有很多代码做了修改。
同时我也是读源码的小白,对于启动流程本来只知道先init再zygote再systemserver再launcher,但是很多时候都不知道哪里的代码进行的过程的推进,也算是对自己知识的精细化。但本篇并没有到特别的深入的层次,例如:AMS启动,SystemServiceManager启动,Launcher获取应用信息的机制,这些没有写,仅仅做了介绍。如果看的师傅感兴趣,我也贴了我觉得写的好的资源,大家可以看看。
可能会有一些造轮子的嫌疑,但毕竟是自己一步步分析的成果,希望能通过这篇自己有些成长。同时也希望和我一样的源码阅读初学者有些助力。
这是在看雪的第一篇博客,很多地方写的有点啰嗦,可能还有不对的地方,希望师傅们不吝赐教。
先来一个总览
按下电源之后,引导芯片代码从预定义的地方开始执行(硬件写死的一个位置)开始执行,加载Bootloader到RAM,开始执行。
在Android操作系统开始前的一个程序,主要作用把系统OS给拉起
之前看权威指南的时候,没有写到这里的这个内核空间的一个进程,但实际上这才是第一个进程,这个进程是在内核空间做初始化的,和启动init进程的。
可以参考一下这个帖子
https://blog.csdn.net/marshal_zsx/article/details/80225854
由上init进程是由内核空间启动的,里面有这样一段代码
需要关心的就是这里的init,文件在system/bin/init
根据mk文件我们可以看到它是如何编译出来的
D:\android-7.0.0_r1\system\core\init\Android.mk
可以看到这里的这个init.cpp
main主要的逻辑不是很长
其实Android7.0我看的这个版本init.cpp的main还是比较简单的,到后面Android10代码结构变了一些,将first stage,second stage,解析的代码全都封装进别的函数了,对分析者来说可能更好懂一些吧。
做一下总结,init这个进程做了什么在init.cpp可以看个大概
这一块稍微有点搞,按照正常流程走的话,里面就到了要开始准备进入zygote相关的逻辑了。
因为Android稍高一些的逻辑是如下这样,可以找到start zygote字样
但对于Android7.0版本
所以暂时刚开始来到这里的时候其实还是去研究了一下的,先说一下,是这里 class_start main 进行启动的
要知道他怎么启动的,需要掌握一些rc的AIL文件和Parser如何解析init.rc相关知识。
Android Init Language 安卓初始化语言,这一块本人了解不深,研究启动流程也不需要太深理解,所以大概看一下就选(我写的有点啰嗦)
主要有五类语句,Actions、Commands、Services、Options、Imports
Actions、Services、Import可以确定一个Section,如下是一个section
格式
后面会包含一个trigger触发器 表明何是执行这个Action
简单来讲就是要执行的命令
复制自https://blog.csdn.net/w2064004678/article/details/105510821
表明一些在初始化时就启动或者退出时需要重启的程序
格式
一个例子
是用来修饰服务的,告诉服务要怎么进行运行
引入配置文件
例子
AIL 的介绍就到这了,如果想要详细了解请阅读 system/core/init 下的 readme.txt 文件。
目光先转到init.cpp里面,下面这段代码对init.rc进行解析
ok,这个parser是怎么写的呢,逻辑就在init_parser.cpp里面
一点点来,既然上面先调用了parser.AddSectionParser,那我们就看看这个函数
可以看到第二个参数parser被保存在以第一个参数name
为标号的section_parsers_里面,而section_parsers__是一个map集合,也就是这个函数的作用是将parser和对应的section进行绑定
其实以上操作很大意义上是在做解释器初始化的工作。
接下来看看解析的这一行parser.ParseConfig("/init.rc");,其中parser.ParseConfig
可以看到这个函数作用就是根据传进来的参数看是文件路径还是目录路径,目录路径就调用ParseConfigDir 进行下一步的递归,找到文件路径然后再调用ParseConfigFile
那么重点来到ParseConfigFile
可以看到下面的作用就是
进一步来到ParseData
代码稍长,把分析写在代码里面了
简单来说上述函数就是通过判断,调用
这根据解释器的不同,逻辑也有些不同,如果你是Action,则在ActionParser ,如果service则在ServiceParser
action.cpp
ParseSection是创建Action对象(我理解下来就是对action的操作) 为对象添加一个触发器 并且将action_移动到目前的Action对象里面去
ParseLineSection 这个就是每一行(我理解下来就是对command的操作)
根据上面的这个function_map_ 查找设置对应的command处理函数
例如启动zygote的class start,就是在这里被设置好 碰见class start的时候调用哪个函数
那具体调用哪个函数呢,继续往下看function_map_在那被复值
需要再次回到init.cpp 找到下面这行
c下面就要找到BuiltinFunctionMap 实在builtins.cpp里面的
我们找到下面这个map
那class_start 对应的就是do_class_start,在builtins里面就写好了
具体的逻辑我们下面再去看,先结束掉ActionParser的ParseLineSection
再是EndSection
来总结一下解析Action的过程
起始大体逻辑和Action是很像的
与Action类似
现在有了AIL的知识,和解释器如何解释每一条的command,现在设备就可以正常读取,并且管理这个init.rc文件当中的内容了(感觉还是在做初始化,还没到运行)
我们的目的是要找到如何启动zygote,那如何执行command也很重要(比如class_start main如何锁定到zygote)
回到init.cpp
可以看到action的执行,是通过ExecuteOneCommand的调用的,往上找,很容易找到是ActionManager的ExecuteOneCommand,进去看
小小总结一下
好,现在基本上了解了AIL,如何解析启动zygote所在的init.rc,init.rc中的command如何运行
通识的东西基本上大差不差,接下来要去看看如何启动zygote(感觉前面有点啰嗦)
还记得之前我说很重要的这个函数吧
里面调用service的StartIfNotDisabled
可以看到又调用了Service 的 Start 函数
代码里的是class-start main
我们看到,里面class的标识就是main
好了,做一下总结
init.cpp做了一些command的初始化,并且在解释器里面把init.rc读入让设备看懂init的初始化配置,然后再在init.cpp里面无限循环中调用init.rc当中的command,其中就包括了zygote的启动
init主要工作
接下来逻辑进入到zygote
到我手上这个pixel3的android10版本是有俩zygote的 分别是两个不同版本
进入到zygote,其实是先进入native层的zygote
首先根据设备的信息启动不同类型的zygote(位数),我以64 32为例
可以看到二进制文件是app_process64和app_process32,-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote都是他的参数
上面这个app_process64和app_process32都被编译完了,我们找到他的源代码看逻辑
代码比较长,我们主要关注三部分
可以看到里面如--start-system-server是我们的参数-Xzygote /system/bin --zygote --start-system-server当中的一部分
他会进行标志位的设置
上面根据我们的参数知道以zygote参数启动这个app_process,里面zygote被设置为true,那么就会进入下面这个判断中,特别别是runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
这玩意就很长了 我先贴出来
里面这两处对理解Android系统还是有很大帮助的,,startVM更是理解虚拟机的入口,标记一下,startreg是用来注册JNI的
然后就到这里 作用是 找到ZygoteInit的main函数,然后通过JNI调用(写法很熟悉了),进入到zygote的Java层
如上 ZygoteInit.java.main方法
先上代码
里面几个重要函数
上个时序图
解析rc的参数
在native层调用运行时的start方法,里面又创建虚拟机和注册JNI函数
通过JNI方式调用ZygoteInit的main函数,进入到java层
registerZygoteSocket注册zygote用的socket用来和AMS进行通信,用来创建新的应用进程
preload预加载的资源、类、虚拟机实例等
startSystemServer启动SystemServer进程,就有之前学过的PMS这些
runSelectLoop循环等待并处理AMS发送来的创建新应用进程请求。
到这里zygote就差不多干完初始化的活了,开始等待
刚刚学到Zygote启动了SyetemServer
我们先上一个时序图
这一块我经理就稍微少一点,也是偷的别的师傅的分析,有个概念先
ZygoteInit.startSystemServer()
fork 子进程 system_server,进入 system_server 进程。
ZygoteInit.handleSystemServerProcess()
设置当前进程名为“system_server”,创建 PathClassLoader 类加载器。
RuntimeInit.zygoteInit()
重定向 log 输出,通用的初始化(设置默认异常捕捉方法,时区等),初始化 Zygote -> nativeZygoteInit()
app_main::onZygoteInit()
proc->startThreadPool(); 启动Binder线程池,这样就可以与其他进程进行通信。
ZygoteInit.main()
开启 DDMS 功能,preload() 加载资源,预加载OpenGL,调用 SystemServer.main() 方法
SystemServer.main()
先初始化 SystemServer 对象,再调用对象的 run() 方法。
到下面这里,就比较熟悉了
startBootstrapServices:引导服务
startCoreServices:核心服务
startOtherServices:其他服务(60多种)
都是一些Android必要的服务
可能写的有点突然,这个Launcher其实是由SystemServer启动的AMS启动的。这个Launcher的作用就是来显示 已经安装的应用程序,Lanucher在启动过程中会请求PackageManagerService返回系统中已经安装的应用程序信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上。
代码逻辑弯弯绕绕,非常长,这里仅作系统启动学习,就不写那么多了,详情可以去看这个师傅的博客
https://blog.csdn.net/fdsafwagdagadg6576/article/details/116333916
这边只做总结
好了,这篇到这里就结束了,有不对的地方请指出,我会在看到的第一时间改正。希望对于阅读的小伙伴们有帮助。
后面应该还会有一些源码阅读的部分写出来,一般是涉及到跟安全强相关的部分。写完这些我认为关键的源码阅读分析之后,会着手多分析分析如Magisk原理,Xposed相关的一些内容,并尽量从底层进行一些分析。
源码版本
android
-
7.0
.
0_r1
链接:https:
/
/
pan.baidu.com
/
s
/
1yla9fqd4EbxSBSemrYVjsA
?pwd
=
ukvt
提取码:ukvt
-
-
来自百度网盘超级会员V3的分享
测试机
pixel3 android10
源码版本
android
-
7.0
.
0_r1
链接:https:
/
/
pan.baidu.com
/
s
/
1yla9fqd4EbxSBSemrYVjsA
?pwd
=
ukvt
提取码:ukvt
-
-
来自百度网盘超级会员V3的分享
测试机
pixel3 android10
https:
/
/
blog.csdn.net
/
qq_43369592
/
article
/
details
/
123113889
https:
/
/
juejin.cn
/
post
/
7232947178690412602
https:
/
/
blog.csdn.net
/
fdsafwagdagadg6576
/
article
/
details
/
116333916
https:
/
/
www.cnblogs.com
/
wanghao
-
boke
/
p
/
18099022
https:
/
/
juejin.cn
/
post
/
6844903506915098637
《Android进阶解密》 刘望舒
《深入理解Android Java虚拟机ART》邓凡平
https:
/
/
blog.csdn.net
/
qq_43369592
/
article
/
details
/
123113889
https:
/
/
juejin.cn
/
post
/
7232947178690412602
https:
/
/
blog.csdn.net
/
fdsafwagdagadg6576
/
article
/
details
/
116333916
https:
/
/
www.cnblogs.com
/
wanghao
-
boke
/
p
/
18099022
https:
/
/
juejin.cn
/
post
/
6844903506915098637
《Android进阶解密》 刘望舒
《深入理解Android Java虚拟机ART》邓凡平
提醒:
建议阅读本篇文章的时候先读每一个部分的总结(如
4
,
5
,
6
的总结,有总结的一般是我觉得比较复杂的),有源码阅读经验的师傅应该晓得有些时候我们跟源码的时候可能会忘了为什么跟到这里,所以可以先看总结,以了解写某块分析的目的。
第四部分init当中
4.2
.
1
和
4.2
.
2
可能会有些啰嗦和抽象,可以自己看看源码,会有一些收获。
提醒:
建议阅读本篇文章的时候先读每一个部分的总结(如
4
,
5
,
6
的总结,有总结的一般是我觉得比较复杂的),有源码阅读经验的师傅应该晓得有些时候我们跟源码的时候可能会忘了为什么跟到这里,所以可以先看总结,以了解写某块分析的目的。
第四部分init当中
4.2
.
1
和
4.2
.
2
可能会有些啰嗦和抽象,可以自己看看源码,会有一些收获。
这一块经常root的朋友应该接触的比较多,我对这个研究比较浅,但是冲浪的时候看到了一个比较有趣比较猛的Bootloader破解的方式
https:
/
/
www.
4hou
.com
/
posts
/
5VBq
这一块经常root的朋友应该接触的比较多,我对这个研究比较浅,但是冲浪的时候看到了一个比较有趣比较猛的Bootloader破解的方式
https:
/
/
www.
4hou
.com
/
posts
/
5VBq
if
(!try_to_run_init_process(
"/sbin/init"
) ||
!try_to_run_init_process(
"/etc/init"
) ||
!try_to_run_init_process(
"/bin/init"
) ||
!try_to_run_init_process(
"/bin/sh"
))
return
0
;
if
(!try_to_run_init_process(
"/sbin/init"
) ||
!try_to_run_init_process(
"/etc/init"
) ||
!try_to_run_init_process(
"/bin/init"
) ||
!try_to_run_init_process(
"/bin/sh"
))
return
0
;
LOCAL_SRC_FILES:
=
\
bootchart.cpp \
builtins.cpp \
devices.cpp \
init.cpp \
keychords.cpp \
property_service.cpp \
signal_handler.cpp \
ueventd.cpp \
ueventd_parser.cpp \
watchdogd.cpp \
LOCAL_SRC_FILES:
=
\
bootchart.cpp \
builtins.cpp \
devices.cpp \
init.cpp \
keychords.cpp \
property_service.cpp \
signal_handler.cpp \
ueventd.cpp \
ueventd_parser.cpp \
watchdogd.cpp \
int
main(
int
argc,
char
** argv) {
if
(!
strcmp
(basename(argv[0]),
"ueventd"
)) {
return
ueventd_main(argc, argv);
}
if
(!
strcmp
(basename(argv[0]),
"watchdogd"
)) {
return
watchdogd_main(argc, argv);
}
umask(0);
add_environment(
"PATH"
, _PATH_DEFPATH);
bool
is_first_stage = (argc == 1) || (
strcmp
(argv[1],
"--second-stage"
) != 0);
if
(is_first_stage) {
mount(
"tmpfs"
,
"/dev"
,
"tmpfs"
, MS_NOSUID,
"mode=0755"
);
mkdir(
"/dev/pts"
, 0755);
mkdir(
"/dev/socket"
, 0755);
mount(
"devpts"
,
"/dev/pts"
,
"devpts"
, 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount(
"proc"
,
"/proc"
,
"proc"
, 0,
"hidepid=2,gid="
MAKE_STR(AID_READPROC));
mount(
"sysfs"
,
"/sys"
,
"sysfs"
, 0, NULL);
}
open_devnull_stdio();
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
NOTICE(
"init %s started!\n"
, is_first_stage ?
"first stage"
:
"second stage"
);
if
(!is_first_stage) {
close(open(
"/dev/.booting"
, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
property_init();
process_kernel_dt();
process_kernel_cmdline();
export_kernel_boot_props();
}
selinux_initialize(is_first_stage);
if
(is_first_stage) {
if
(restorecon(
"/init"
) == -1) {
ERROR(
"restorecon failed: %s\n"
,
strerror
(
errno
));
security_failure();
}
char
* path = argv[0];
char
* args[] = { path,
const_cast
<
char
*>(
"--second-stage"
), nullptr };
if
(execv(path, args) == -1) {
ERROR(
"execv(\"%s\") failed: %s\n"
, path,
strerror
(
errno
));
security_failure();
}
}
NOTICE(
"Running restorecon...\n"
);
restorecon(
"/dev"
);
restorecon(
"/dev/socket"
);
restorecon(
"/dev/__properties__"
);
restorecon(
"/property_contexts"
);
restorecon_recursive(
"/sys"
);
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if
(epoll_fd == -1) {
ERROR(
"epoll_create1 failed: %s\n"
,
strerror
(
errno
));
exit
(1);
}
signal_handler_init();
property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
const
BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
Parser& parser = Parser::GetInstance();
parser.AddSectionParser(
"service"
,std::make_unique<ServiceParser>());
parser.AddSectionParser(
"on"
, std::make_unique<ActionParser>());
parser.AddSectionParser(
"import"
, std::make_unique<ImportParser>());
parser.ParseConfig(
"/init.rc"
);
ActionManager& am = ActionManager::GetInstance();
am.QueueEventTrigger(
"early-init"
);
am.QueueBuiltinAction(wait_for_coldboot_done_action,
"wait_for_coldboot_done"
);
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng"
);
am.QueueBuiltinAction(set_mmap_rnd_bits_action,
"set_mmap_rnd_bits"
);
am.QueueBuiltinAction(keychord_init_action,
"keychord_init"
);
am.QueueBuiltinAction(console_init_action,
"console_init"
);
am.QueueEventTrigger(
"init"
);
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng"
);
std::string bootmode = property_get(
"ro.bootmode"
);
if
(bootmode ==
"charger"
) {
am.QueueEventTrigger(
"charger"
);
}
else
{
am.QueueEventTrigger(
"late-init"
);
}
am.QueueBuiltinAction(queue_property_triggers_action,
"queue_property_triggers"
);
while
(
true
) {
if
(!waiting_for_exec) {
am.ExecuteOneCommand();
restart_processes();
}
int
timeout = -1;
if
(process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if
(timeout < 0)
timeout = 0;
}
if
(am.HasMoreCommands()) {
timeout = 0;
}
bootchart_sample(&timeout);
epoll_event ev;
int
nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if
(nr == -1) {
ERROR(
"epoll_wait failed: %s\n"
,
strerror
(
errno
));
}
else
if
(nr == 1) {
((
void
(*)()) ev.data.ptr)();
}
}
return
0;
}
int
main(
int
argc,
char
** argv) {
if
(!
strcmp
(basename(argv[0]),
"ueventd"
)) {
return
ueventd_main(argc, argv);
}
if
(!
strcmp
(basename(argv[0]),
"watchdogd"
)) {
return
watchdogd_main(argc, argv);
}
umask(0);
add_environment(
"PATH"
, _PATH_DEFPATH);
bool
is_first_stage = (argc == 1) || (
strcmp
(argv[1],
"--second-stage"
) != 0);
if
(is_first_stage) {
mount(
"tmpfs"
,
"/dev"
,
"tmpfs"
, MS_NOSUID,
"mode=0755"
);
mkdir(
"/dev/pts"
, 0755);
mkdir(
"/dev/socket"
, 0755);
mount(
"devpts"
,
"/dev/pts"
,
"devpts"
, 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount(
"proc"
,
"/proc"
,
"proc"
, 0,
"hidepid=2,gid="
MAKE_STR(AID_READPROC));
mount(
"sysfs"
,
"/sys"
,
"sysfs"
, 0, NULL);
}
open_devnull_stdio();
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
NOTICE(
"init %s started!\n"
, is_first_stage ?
"first stage"
:
"second stage"
);
if
(!is_first_stage) {
close(open(
"/dev/.booting"
, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
property_init();
process_kernel_dt();
process_kernel_cmdline();
export_kernel_boot_props();
}
selinux_initialize(is_first_stage);
if
(is_first_stage) {
if
(restorecon(
"/init"
) == -1) {
ERROR(
"restorecon failed: %s\n"
,
strerror
(
errno
));
security_failure();
}
char
* path = argv[0];
char
* args[] = { path,
const_cast
<
char
*>(
"--second-stage"
), nullptr };
if
(execv(path, args) == -1) {
ERROR(
"execv(\"%s\") failed: %s\n"
, path,
strerror
(
errno
));
security_failure();
}
}
NOTICE(
"Running restorecon...\n"
);
restorecon(
"/dev"
);
restorecon(
"/dev/socket"
);
restorecon(
"/dev/__properties__"
);
restorecon(
"/property_contexts"
);
restorecon_recursive(
"/sys"
);
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if
(epoll_fd == -1) {
ERROR(
"epoll_create1 failed: %s\n"
,
strerror
(
errno
));
exit
(1);
}
signal_handler_init();
property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
const
BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
Parser& parser = Parser::GetInstance();
parser.AddSectionParser(
"service"
,std::make_unique<ServiceParser>());
parser.AddSectionParser(
"on"
, std::make_unique<ActionParser>());
parser.AddSectionParser(
"import"
, std::make_unique<ImportParser>());
parser.ParseConfig(
"/init.rc"
);
ActionManager& am = ActionManager::GetInstance();
am.QueueEventTrigger(
"early-init"
);
am.QueueBuiltinAction(wait_for_coldboot_done_action,
"wait_for_coldboot_done"
);
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng"
);
am.QueueBuiltinAction(set_mmap_rnd_bits_action,
"set_mmap_rnd_bits"
);
am.QueueBuiltinAction(keychord_init_action,
"keychord_init"
);
am.QueueBuiltinAction(console_init_action,
"console_init"
);
am.QueueEventTrigger(
"init"
);
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng"
);
std::string bootmode = property_get(
"ro.bootmode"
);
if
(bootmode ==
"charger"
) {
am.QueueEventTrigger(
"charger"
);
}
else
{
am.QueueEventTrigger(
"late-init"
);
}
am.QueueBuiltinAction(queue_property_triggers_action,
"queue_property_triggers"
);
while
(
true
) {
if
(!waiting_for_exec) {
am.ExecuteOneCommand();
restart_processes();
}
int
timeout = -1;
if
(process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if
(timeout < 0)
timeout = 0;
}
if
(am.HasMoreCommands()) {
timeout = 0;
}
bootchart_sample(&timeout);
epoll_event ev;
int
nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if
(nr == -1) {
ERROR(
"epoll_wait failed: %s\n"
,
strerror
(
errno
));
}
else
if
(nr == 1) {
((
void
(*)()) ev.data.ptr)();
}
}
return
0;
}
on boot
ifup lo
hostname localhost
domainname localdomain
on boot
ifup lo
hostname localhost
domainname localdomain
on <trgger> [&& <trigger>]
*
<command>
<command>
<command>
...
on <trgger> [&& <trigger>]
*
<command>
<command>
<command>
...
命令 |
解释 |
bootchart_init |
如果配置了bootcharing,则启动.包含在默认的init.rc中 |
chmod |
更改文件权限 |
chown <owner> <group> <path> |
更改文件的所有者和组 |
calss_start <serviceclass> |
启动指定类别服务下的所有未启动的服务 |
class_stop <serviceclass> |
停止指定类别服务类下的所有已运行的服务 |
class_reset <serviceclass> |
停止指定类别的所有服务(服务还在运行),但不会禁用这些服务.后面可以通过class_start重启这些服务 |
copy <src> <dst> |
复制文件,对二进制/大文件非常有用 |
domainname <name> |
设置域名称 |
enable <servicename> |
启用已经禁用的服务 |
exec [ <seclabel> [ <user> [ <group> ]* ]] --<command> [ <argument> ]*
|
fork一个进程执行指定命令,如果有参数,则带参数执行 |
export <name> |
在全局环境中,将<name> 变量的值设置为<value> ,即以键值对的方式设置全局环境变量.这些变量对之后的任何进程都有效 |
hostname |
设置主机名 |
ifup <interface> |
启动某个网络接口 |
insmod [-f] <path> [<options>] |
加载指定路径下的驱动模块。-f强制加载,即不管当前模块是否和linux kernel匹配 |
load_all_props |
从/system,/vendor加载属性。默认包含在init.rc |
load_persist_props |
当/data被加密时,加载固定属性 |
loglevel <level> |
设置kernel日志等级 |
mkdir <path> [mode] [owner] [group] |
在制定路径下创建目录 |
mount_all <fstab> [ <path> ]* |
在给定的fs_mgr-format上调用fs_mgr_mount和引入rc文件 |
mount <type> <device> <dir>[ <flag> ]* [<options>] |
挂载指定设备到指定目录下. |
powerct |
用来应对sys.powerctl中系统属性的变化,用于系统重启 |
restart <service> |
重启制定服务,但不会禁用该服务 |
restorecon <path> [ <path> ]* |
恢复指定文件到file_contexts配置中指定的安全上线文环境 |
restorecon_recursive <path> [ <path> ]* |
以递归的方式恢复指定目录到file_contexts配置中指定的安全上下文中 |
rm <path> |
删除指定路径下的文件 |
rmdir <path> |
删除制定路径下的目录 |
setprop <name> <value> |
将系统属性<name> 的值设置为<value> ,即以键值对的方式设置系统属性 |
setrlimit <resource> <cur> <max> |
设置资源限制 |
start <service> |
启动服务(如果该服务还未启动) |
stop <service> |
关闭服务(如果该服务还未停止) |
swapon_all <fstab> |
|
symlink <target> <path> |
创建一个指向<path> 的符合链接<target>
|
sysclktz <mins_west_of_gmt> |
设置系统时钟的基准,比如0代表GMT,即以格林尼治时间为准 |
trigger <event> |
触发一个事件,将该action排在某个action之后(用于Action排队) |
verity_load_state |
|
verity_update_state <mount_point> |
|
wait <path> [ <timeout> ] |
等待一个文件是否存在,存在时立刻返回或者超时后返回.默认超时事件是5s |
write <path> <content> |
写内容到指定文件中 |
service <name> <pathname> [ <argument> ]
*
<option>
<option>
...
service <name> <pathname> [ <argument> ]
*
<option>
<option>
...
service ueventd
/
sbin
/
ueventd
class
core
critical
seclabel u:r:ueventd:s0
service ueventd
/
sbin
/
ueventd
class
core
critical
seclabel u:r:ueventd:s0
选项 |
解释 |
console |
服务需要一个控制台. |
critical |
表示这是一个关键设备服务.如果4分钟内此服务退出4次以上,那么这个设备将重启进入recovery模式 |
disabled |
服务不会自动启动,必须通过服务名显式启动 |
setenv <name> <value> |
在进程启动过程中,将环境变量<name> 的值设置为<value> ,即以键值对的方式设置环境变量 |
socket <name> <type> <perm> [ <user> [ <group> [seclabel]]] |
创建一个unix域下的socket,其被命名/dev/socket/<name> . 并将其文件描述符fd返回给服务进程.其中,type必须为dgram,stream或者seqpacke,user和group默认是0.seclabel是该socket的SELLinux的安全上下文环境,默认是当前service的上下文环境,通过seclabel指定. |
user <username> |
在执行此服务之前切换用户名,当前默认的是root.自Android M开始,即使它要求linux capabilities,也应该使用该选项.很明显,为了获得该功能,进程需要以root用户运行 |
group <groupname> |
在执行此服务之前切换组名,除了第一个必须的组名外,附加的组名用于设置进程的补充组(借助setgroup()函数),当前默认的是root |
seclabel <seclabel> |
在执行该服务之前修改其安全上下文,默认是init程序的上下文 |
oneshot |
当服务退出时,不重启该服务 |
class <name> |
为当前service设定一个类别.相同类别的服务将会同时启动或者停止,默认类名是default. |
onrestart |
当服务重启时执行该命令 |
priority <priority> |
设置服务进程的优先级.优先级取值范围为-20~19,默认是0.可以通过setpriority()设置 |
import
<path>
import
/
init.environ.rc
import
/
init.usb.rc
import
/
init.${ro.hardware}.rc
import
/
init.usb.configfs.rc
import
/
init.${ro.zygote}.rc
import
/
init.environ.rc
import
/
init.usb.rc
import
/
init.${ro.hardware}.rc
import
/
init.usb.configfs.rc
import
/
init.${ro.zygote}.rc
Parser& parser = Parser::GetInstance();
parser.AddSectionParser(
"service"
,std::make_unique<ServiceParser>());
parser.AddSectionParser(
"on"
, std::make_unique<ActionParser>());
parser.AddSectionParser(
"import"
, std::make_unique<ImportParser>());
parser.ParseConfig(
"/init.rc"
);
Parser& parser = Parser::GetInstance();
parser.AddSectionParser(
"service"
,std::make_unique<ServiceParser>());
parser.AddSectionParser(
"on"
, std::make_unique<ActionParser>());
parser.AddSectionParser(
"import"
, std::make_unique<ImportParser>());
parser.ParseConfig(
"/init.rc"
);
void
Parser::AddSectionParser(
const
std::string& name,
std::unique_ptr<SectionParser> parser) {
section_parsers_[name] = std::move(parser);
}
void
Parser::AddSectionParser(
const
std::string& name,
std::unique_ptr<SectionParser> parser) {
section_parsers_[name] = std::move(parser);
}
bool
Parser::ParseConfig(
const
std::string& path) {
if
(is_dir(path.c_str())) {
return
ParseConfigDir(path);
}
return
ParseConfigFile(path);
}
bool
Parser::ParseConfigDir(
const
std::string& path) {
INFO(
"Parsing directory %s...\n"
, path.c_str());
std::unique_ptr<DIR,
int
(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
if
(!config_dir) {
ERROR(
"Could not import directory '%s'\n"
, path.c_str());
return
false
;
}
dirent* current_file;
while
((current_file = readdir(config_dir.get()))) {
std::string current_path =
android::base::StringPrintf(
"%s/%s"
, path.c_str(), current_file->d_name);
if
(current_file->d_type == DT_REG) {
if
(!ParseConfigFile(current_path)) {
ERROR(
"could not import file '%s'\n"
, current_path.c_str());
}
}
}
return
true
;
}
bool
Parser::ParseConfig(
const
std::string& path) {
if
(is_dir(path.c_str())) {
return
ParseConfigDir(path);
}
return
ParseConfigFile(path);
}
bool
Parser::ParseConfigDir(
const
std::string& path) {
INFO(
"Parsing directory %s...\n"
, path.c_str());
std::unique_ptr<DIR,
int
(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
if
(!config_dir) {
ERROR(
"Could not import directory '%s'\n"
, path.c_str());
return
false
;
}
dirent* current_file;
while
((current_file = readdir(config_dir.get()))) {
std::string current_path =
android::base::StringPrintf(
"%s/%s"
, path.c_str(), current_file->d_name);
if
(current_file->d_type == DT_REG) {
if
(!ParseConfigFile(current_path)) {
ERROR(
"could not import file '%s'\n"
, current_path.c_str());
}
}
}
return
true
;
}
bool
Parser::ParseConfigFile(
const
std::string& path) {
INFO(
"Parsing file %s...\n"
, path.c_str());
Timer t;
std::string data;
if
(!read_file(path.c_str(), &data)) {
return
false
;
}
data.push_back(
'\n'
);
ParseData(path, data);
for
(
const
auto
& sp : section_parsers_) {
sp.second->EndFile(path);
}
if
(
false
) DumpState();
NOTICE(
"(Parsing %s took %.2fs.)\n"
, path.c_str(), t.duration());
return
true
;
}
bool
Parser::ParseConfigFile(
const
std::string& path) {
INFO(
"Parsing file %s...\n"
, path.c_str());
Timer t;
std::string data;
if
(!read_file(path.c_str(), &data)) {
return
false
;
}
data.push_back(
'\n'
);
ParseData(path, data);
for
(
const
auto
& sp : section_parsers_) {
sp.second->EndFile(path);
}
if
(
false
) DumpState();
NOTICE(
"(Parsing %s took %.2fs.)\n"
, path.c_str(), t.duration());
return
true
;
}
void
Parser::ParseData(
const
std::string& filename,
const
std::string& data) {
std::vector<
char
> data_copy(data.begin(), data.end());
data_copy.push_back(
'\0'
);
parse_state state;
state.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
std::vector<std::string> args;
for
(;;) {
switch
(next_token(&state)) {
case
T_EOF:
if
(section_parser) {
section_parser->EndSection();
}
return
;
case
T_NEWLINE:
state.line++;
if
(args.empty()) {
break
;
}
if
(section_parsers_.count(args[0])) {
if
(section_parser) {
section_parser->EndSection();
}
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
if
(!section_parser->ParseSection(args, &ret_err)) {
parse_error(&state,
"%s\n"
, ret_err.c_str());
section_parser = nullptr;
}
}
else
if
(section_parser) {
std::string ret_err;
if
(!section_parser->ParseLineSection(args, state.filename,
state.line, &ret_err)) {
parse_error(&state,
"%s\n"
, ret_err.c_str());
}
}
args.clear();
break
;
case
T_TEXT:
args.emplace_back(state.text);
break
;
}
}
}
void
Parser::ParseData(
const
std::string& filename,
const
std::string& data) {
std::vector<
char
> data_copy(data.begin(), data.end());
data_copy.push_back(
'\0'
);
parse_state state;
state.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
std::vector<std::string> args;
for
(;;) {
switch
(next_token(&state)) {
case
T_EOF:
if
(section_parser) {
section_parser->EndSection();
}
return
;
case
T_NEWLINE:
state.line++;
if
(args.empty()) {
break
;
}
if
(section_parsers_.count(args[0])) {
if
(section_parser) {
section_parser->EndSection();
}
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
if
(!section_parser->ParseSection(args, &ret_err)) {
parse_error(&state,
"%s\n"
, ret_err.c_str());
section_parser = nullptr;
}
}
else
if
(section_parser) {
std::string ret_err;
if
(!section_parser->ParseLineSection(args, state.filename,
state.line, &ret_err)) {
parse_error(&state,
"%s\n"
, ret_err.c_str());
}
}
args.clear();
break
;
case
T_TEXT:
args.emplace_back(state.text);
break
;
}
}
}
bool
ActionParser::ParseSection(
const
std::vector<std::string>& args,
std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if
(triggers.size() < 1) {
*err =
"actions must have a trigger"
;
return
false
;
}
auto
action = std::make_unique<Action>(
false
);
if
(!action->InitTriggers(triggers, err)) {
return
false
;
}
action_ = std::move(action);
return
true
;
}
bool
ActionParser::ParseSection(
const
std::vector<std::string>& args,
std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if
(triggers.size() < 1) {
*err =
"actions must have a trigger"
;
return
false
;
}
auto
action = std::make_unique<Action>(
false
);
if
(!action->InitTriggers(triggers, err)) {
return
false
;
}
action_ = std::move(action);
return
true
;
}
bool
ActionParser::ParseLineSection(
const
std::vector<std::string>& args,
const
std::string& filename,
int
line,
std::string* err)
const
{
return
action_ ? action_->AddCommand(args, filename, line, err) :
false
;
}
调用下面这个AddCommand
bool
Action::AddCommand(
const
std::vector<std::string>& args,
const
std::string& filename,
int
line, std::string* err) {
if
(!function_map_) {
*err =
"no function map available"
;
return
false
;
}
if
(args.empty()) {
*err =
"command needed, but not provided"
;
return
false
;
}
auto
function = function_map_->FindFunction(args[0], args.size() - 1, err);
if
(!function) {
return
false
;
}
AddCommand(function, args, filename, line);
return
true
;
}
在调用下面这个AddCommand
void
Action::AddCommand(BuiltinFunction f,
const
std::vector<std::string>& args,
const
std::string& filename,
int
line) {
commands_.emplace_back(f, args, filename, line);
}
bool
ActionParser::ParseLineSection(
const
std::vector<std::string>& args,
const
std::string& filename,
int
line,
std::string* err)
const
{
return
action_ ? action_->AddCommand(args, filename, line, err) :
false
;
}
调用下面这个AddCommand
bool
Action::AddCommand(
const
std::vector<std::string>& args,
const
std::string& filename,
int
line, std::string* err) {
if
(!function_map_) {
*err =
"no function map available"
;
return
false
;
}
if
(args.empty()) {
*err =
"command needed, but not provided"
;
return
false
;
}
auto
function = function_map_->FindFunction(args[0], args.size() - 1, err);
if
(!function) {
return
false
;
}
AddCommand(function, args, filename, line);
return
true
;
}
在调用下面这个AddCommand
void
Action::AddCommand(BuiltinFunction f,
const
std::vector<std::string>& args,
const
std::string& filename,
int
line) {
commands_.emplace_back(f, args, filename, line);
}
static
const
KeywordMap<BuiltinFunction>* function_map_;
static
void
set_function_map(
const
KeywordMap<BuiltinFunction>* function_map) {
function_map_ = function_map;
}
static
const
KeywordMap<BuiltinFunction>* function_map_;
static
void
set_function_map(
const
KeywordMap<BuiltinFunction>* function_map) {
function_map_ = function_map;
}
const
BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
const
BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
BuiltinFunctionMap::Map& BuiltinFunctionMap::map()
const
{
constexpr std::
size_t
kMax = std::numeric_limits<std::
size_t
>::max();
static
const
Map builtin_functions = {
{
"bootchart_init"
, {0, 0, do_bootchart_init}},
{
"chmod"
, {2, 2, do_chmod}},
{
"chown"
, {2, 3, do_chown}},
{
"class_reset"
, {1, 1, do_class_reset}},
{
"class_start"
, {1, 1, do_class_start}},
{
"class_stop"
, {1, 1, do_class_stop}},
{
"copy"
, {2, 2, do_copy}},
{
"domainname"
, {1, 1, do_domainname}},
{
"enable"
, {1, 1, do_enable}},
{
"exec"
, {1, kMax, do_exec}},
{
"export"
, {2, 2, do_export}},
{
"hostname"
, {1, 1, do_hostname}},
{
"ifup"
, {1, 1, do_ifup}},
{
"init_user0"
, {0, 0, do_init_user0}},
{
"insmod"
, {1, kMax, do_insmod}},
{
"installkey"
, {1, 1, do_installkey}},
{
"load_persist_props"
, {0, 0, do_load_persist_props}},
{
"load_system_props"
, {0, 0, do_load_system_props}},
{
"loglevel"
, {1, 1, do_loglevel}},
{
"mkdir"
, {1, 4, do_mkdir}},
{
"mount_all"
, {1, kMax, do_mount_all}},
{
"mount"
, {3, kMax, do_mount}},
{
"powerctl"
, {1, 1, do_powerctl}},
{
"restart"
, {1, 1, do_restart}},
{
"restorecon"
, {1, kMax, do_restorecon}},
{
"restorecon_recursive"
, {1, kMax, do_restorecon_recursive}},
{
"rm"
, {1, 1, do_rm}},
{
"rmdir"
, {1, 1, do_rmdir}},
{
"setprop"
, {2, 2, do_setprop}},
{
"setrlimit"
, {3, 3, do_setrlimit}},
{
"start"
, {1, 1, do_start}},
{
"stop"
, {1, 1, do_stop}},
{
"swapon_all"
, {1, 1, do_swapon_all}},
{
"symlink"
, {2, 2, do_symlink}},
{
"sysclktz"
, {1, 1, do_sysclktz}},
{
"trigger"
, {1, 1, do_trigger}},
{
"verity_load_state"
, {0, 0, do_verity_load_state}},
{
"verity_update_state"
, {0, 0, do_verity_update_state}},
{
"wait"
, {1, 2, do_wait}},
{
"write"
, {2, 2, do_write}},
};
return
builtin_functions;
}
BuiltinFunctionMap::Map& BuiltinFunctionMap::map()
const
{
constexpr std::
size_t
kMax = std::numeric_limits<std::
size_t
>::max();
static
const
Map builtin_functions = {
{
"bootchart_init"
, {0, 0, do_bootchart_init}},
{
"chmod"
, {2, 2, do_chmod}},
{
"chown"
, {2, 3, do_chown}},
{
"class_reset"
, {1, 1, do_class_reset}},
{
"class_start"
, {1, 1, do_class_start}},
{
"class_stop"
, {1, 1, do_class_stop}},
{
"copy"
, {2, 2, do_copy}},
{
"domainname"
, {1, 1, do_domainname}},
{
"enable"
, {1, 1, do_enable}},
{
"exec"
, {1, kMax, do_exec}},
{
"export"
, {2, 2, do_export}},
{
"hostname"
, {1, 1, do_hostname}},
{
"ifup"
, {1, 1, do_ifup}},
{
"init_user0"
, {0, 0, do_init_user0}},
{
"insmod"
, {1, kMax, do_insmod}},
{
"installkey"
, {1, 1, do_installkey}},
{
"load_persist_props"
, {0, 0, do_load_persist_props}},
{
"load_system_props"
, {0, 0, do_load_system_props}},
{
"loglevel"
, {1, 1, do_loglevel}},
{
"mkdir"
, {1, 4, do_mkdir}},
{
"mount_all"
, {1, kMax, do_mount_all}},
{
"mount"
, {3, kMax, do_mount}},
{
"powerctl"
, {1, 1, do_powerctl}},
{
"restart"
, {1, 1, do_restart}},
{
"restorecon"
, {1, kMax, do_restorecon}},
{
"restorecon_recursive"
, {1, kMax, do_restorecon_recursive}},
{
"rm"
, {1, 1, do_rm}},
{
"rmdir"
, {1, 1, do_rmdir}},
{
"setprop"
, {2, 2, do_setprop}},
{
"setrlimit"
, {3, 3, do_setrlimit}},
{
"start"
, {1, 1, do_start}},
{
"stop"
, {1, 1, do_stop}},
{
"swapon_all"
, {1, 1, do_swapon_all}},
{
"symlink"
, {2, 2, do_symlink}},
{
"sysclktz"
, {1, 1, do_sysclktz}},
{
"trigger"
, {1, 1, do_trigger}},
{
"verity_load_state"
, {0, 0, do_verity_load_state}},
{
"verity_update_state"
, {0, 0, do_verity_update_state}},
{
"wait"
, {1, 2, do_wait}},
{
"write"
, {2, 2, do_write}},
};
return
builtin_functions;
}
void
ActionParser::EndSection() {
if
(action_ && action_->NumCommands() > 0) {
ActionManager::GetInstance().AddAction(std::move(action_));
}
}
void
ActionManager::AddAction(std::unique_ptr<Action> action) {
...
if
(old_action_it != actions_.end()) {
(*old_action_it)->CombineAction(*action);
}
else
{
actions_.emplace_back(std::move(action));
}
}
class
ActionManager {
public
:
static
ActionManager& GetInstance();
void
AddAction(std::unique_ptr<Action> action);
void
QueueEventTrigger(
const
std::string& trigger);
void
QueuePropertyTrigger(
const
std::string& name,
const
std::string& value);
void
QueueAllPropertyTriggers();
void
QueueBuiltinAction(BuiltinFunction func,
const
std::string& name);
void
ExecuteOneCommand();
bool
HasMoreCommands()
const
;
void
DumpState()
const
;
private
:
ActionManager();
ActionManager(ActionManager
const
&) =
delete
;
void
operator=(ActionManager
const
&) =
delete
;
std::vector<std::unique_ptr<Action>> actions_;
std::queue<std::unique_ptr<Trigger>> trigger_queue_;
std::queue<
const
Action*> current_executing_actions_;
std::
size_t
current_command_;
};
void
ActionParser::EndSection() {
if
(action_ && action_->NumCommands() > 0) {
ActionManager::GetInstance().AddAction(std::move(action_));
}
}
void
ActionManager::AddAction(std::unique_ptr<Action> action) {
...
if
(old_action_it != actions_.end()) {
(*old_action_it)->CombineAction(*action);
}
else
{
actions_.emplace_back(std::move(action));
}
}
class
ActionManager {
public
:
static
ActionManager& GetInstance();
void
AddAction(std::unique_ptr<Action> action);
void
QueueEventTrigger(
const
std::string& trigger);
void
QueuePropertyTrigger(
const
std::string& name,
const
std::string& value);
void
QueueAllPropertyTriggers();
void
QueueBuiltinAction(BuiltinFunction func,
const
std::string& name);
void
ExecuteOneCommand();
bool
HasMoreCommands()
const
;
void
DumpState()
const
;
private
:
ActionManager();
ActionManager(ActionManager
const
&) =
delete
;
void
operator=(ActionManager
const
&) =
delete
;
std::vector<std::unique_ptr<Action>> actions_;
std::queue<std::unique_ptr<Trigger>> trigger_queue_;
std::queue<
const
Action*> current_executing_actions_;
std::
size_t
current_command_;
};
bool
ServiceParser::ParseSection(
const
std::vector<std::string>& args,
std::string* err) {
...
const
std::string& name = args[1];
...
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name,
"default"
, str_args);
return
true
;
}
bool
ServiceParser::ParseSection(
const
std::vector<std::string>& args,
std::string* err) {
...
const
std::string& name = args[1];
...
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!