本文代码环境为 Android7.1
概述:
init是一个进程,确切的说,他是Linux系统中用用户空间的第一个进程。由于Android是基于Linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号是1,作为天字一号
进程,init被赋予了很多极其重要的工作职责,本文重点关注其中两个比较关键的职责:
- 1 init进程负责创建系统中的几个关键进程,如 zygote,他是JAVA世界的开创者。那么进程时如何创建 zygote的呢?
- 2 Android系统有很多属性,于是init就提供了一个 property service(属性服务)来管理它们,那么这个属性服务是怎么工作的呢?
一 init 分析
init进程的入口函数是 main (/system/core/init/init.cpp),代码如下:
int main(int argc, char** argv) {
/*
basename()用于返回一个字符串参数的基本文件名称
传入到mian函数的命令行参数有 ueventd 和 watchdogd ,通过比较它们的值,进入到相应的main函数中,启动uevent和watchdog进程,分别
是冷插拔进程和看门狗进程,ueventd伺服程序将解析/ueventd.rc文件,并创建相应的设备结点。 watchdogd伺服程序是一个看门狗程序,它
的任务就是定期向看门狗设备文件执行写操作,以判断系统是否正常运行。由于 ueventd watchdogd 是公用代码,所以启动的时候根据文件
名来判断是哪一个进程
*/
/*****************************第一部分*********************************************/
/*
启动 ueventd 进程,主要用于负责建立Android中设备结点文件,权限设定等一些列工作。服务通过使用 ueventd,监控驱动发送的消息,做进一步的处理。
*/
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
/*****************************第二部分*********************************************/
/*
启动 watchdogd 进程,即启动看门狗进程,判断系统是否正常运行
*/
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
/*****************************第三部分*********************************************/
/*
设置允许当前进程创建文件或者目录最大可操作的权限
*/
umask(0);
//添加环境变量
add_environment("PATH", _PATH_DEFPATH);
/*****************************第四部分*********************************************/
/*
首先构建dev、proc、sys三个主要目录,然后挂载了tmpfs,devpts,proc,sysfs这4类文件系统
*/
bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
/*
创建文件系统目录并挂载文件系统
首先构建dev、proc、sys三个主要目录 (其他目录从init.rc中读取建立),再调用mount函数对文件系统挂载。在这里分别挂载了
tmpfs、devpts、proc、sysfs四类文件系统。
int mount (const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data)
mount说明:
第一个参数:source是要挂上的文件系统,通常是设备名
第二个参数:target是文件系统所要挂载的目标目录
第三个参数:filesystemtype是文件系统类型,只要有“ext2”、“ext3”、“proc”、“sysfs”、“ntfs”、“tmpfs”等
第四个参数:mountflags是文件系统的读写访问标志;
第五个参数:data是文件系统特有的参数
tmpfs:是一种虚拟内存文件系统,典型的tmpfs文件系统是完全驻留在ARM中(虚拟内存)的,因此读写速度远远快于闪存和硬盘文件系统。并且tmpfs下的内容均为临时性内容,
因此如果将tmpfs卸载后,其里面的内容将不再存在。
devpts:是一种远程虚拟终端文件设备,标准挂载点是/dev/pts。只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文件。通过对相应的设
备文件进行操作,可以达到操作硬件的目的(读写)。
proc:是一种虚拟的文件系统,只存在内存中,而不占用内存空间。它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内
核参数。通过echo可以修改内核参数
sysfs:是Linux中设计的一种较新的虚拟文件系统,与proc类似,除了与proc一样查看和设置内核参数之外,还为Linux统一设备管理模型之用
*/
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);
}
/*****************************第五部分*********************************************/
/*
屏蔽标准的输入输出,并且初始化android自己的log系统
*/
//屏蔽标准的输入输出,即标准的输入输出重定向到null设备(/dev/__null__)。
/*
在/dev目录下生成__null__设备节点文件,并将标准输入,标准输出,标准错误,标准错误输出全部重定向__null__设备中。
*/
open_devnull_stdio();
/*
初始化内核log系统。生成"/dev/__kmsg__"设备节点文件, 作为init的日志输出设备(/dev/kmsg),设置完成后马上调用unlink函数,其他进程就无法打开这个文件读取日志信息
了。这个设备会把它收到的任何写入都作为printk的输出,printk是内核中运行的向控制台输出显示的函数
*/
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
/*****************************第六部分*********************************************/
/*
初始化Android系统所需的属性,属性包括设备名字,蓝牙名字,编译信息,网络dns地址,以及其他的一些基本信息等,初始化的方式就是创建一个tmfs文件系统文件/dev/properties
*/
NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
if (!is_first_stage) {
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
/*
初始化属性域。在Android平台中,为了让运行中的所有进程共享系统运行时所需要的各种设置值,系统开辟了属性存储区域,并提供了访问该区域的API。
*/
property_init();
/*
process_kernel_dt 初始化DT(Device Tree)的属性集,即访问“/proc/device-tree/firmware/android ”目录,先看compatible文件内容是否是“android,firmware”,然后这个
目录下每个文件名作为属性,文件里面的内容作为属性值。
*/
process_kernel_dt();
/*
处理内核命令行,读取“/proc/cmdline”文件中的内容,然后调用import_kernel_nv()函数设置系统属性,此处只将“/proc/cmdline”中前缀为“androidboot.”的值设置到属性中。DT属
性集优先级高于此处设置的属性。
*/
process_kernel_cmdline();
/*
设置内核启动的变量,根据系统已有的属性设置,如有值则保持不变,否则设置成初始值。
*/
export_kernel_boot_props();
}
/*****************************第七部分*********************************************/
/*
创建SELINUX(SELinux(Security-Enhanced Linux) 是美国国家安全局(NSA)对于强制访问控制的实现,是 Linux历史上最杰出的新安全子系统)
*/
//初始化 selinux ,如果被禁止,则直接返回
selinux_initialize(is_first_stage);
/*
restorecon命令用来恢复SELinux文件属性即恢复文件的安全上下文
*/
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,这个fd是一个通信套接字,当init初始化完毕后,为成为一个不中断for循环进程,即守护进程,这个fd便不断的监听信号,epoll是Linux内核为处理大批量文件描述符而作了改进的poll
*/
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
ERROR("epoll_create1 failed: %s\n", strerror(errno));
exit(1);
}
/*****************************第九部分*********************************************/
/*
信号处理函数,它是设置子进程退出的信号处理函数,当子进程意外退出的时候,Init会去捕获这个进程信息,当捕获到这些异常信息后,就会调用设置的相应的捕获函数来处理
*/
signal_handler_init();
/*****************************第十部分*********************************************/
/*
加载Android系统根目录的default.prop文件,该文件中包含了一些重要属性,在前面的代码中已经进行过属性初始化工作,这儿把文件中的属性值解析并发布到系统中
*/
property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
/*****************************第十一部分*********************************************/
/*
解析init.rc文件以及触发相应的工作
*/
const BuiltinFunctionMap function_map;
//将function_map存放到Action中作为成员属性
Action::set_function_map(&function_map);
//构造出解析文件用的parser对象
Parser& parser = Parser::GetInstance();
/*
将service、on、import一次加入到 section_parsers_ 列表中
以service开头的行交给ServiceParser解析
以on开头的行交给ActionParser解析
以import开头的行交给ImportParser解析
语法:
1 C++中 std::make_unique 相当于new, 它会返回一个std::unique_ptr, 即智能指针,可以自动管理内存
2 unique_ptr持有对对象的独有权,两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作
3 移动操作的函数是 p1=std::move(p) ,这样指针p指向的对象就移动到p1上了
4 接下来的这三句代码都是new一个Parser(解析器),然后将它们放到一个map里存起来
5 ServiceParser、ActionParser、ImportParser分别对应 service action import 的解析
*/
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
//开始实际的init.rc解析过程
parser.ParseConfig("/init.rc");
// 初始化init.rc解析动作的管理类,获取ActionManager对象,需要通过am对命令执行顺序进行控制
ActionManager& am = ActionManager::GetInstance();
//添加触发器early-init,执行on early-init内容
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");
//添加触发器init,执行on init内容,主要包括创建/挂载一些目录,以及symlink等
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");
/*****************************第十二部分*********************************************/
/*
处理添加到运行队列的事件,当Init进程处理完,它就会进入一个循环化身为守护进程,处理socket接收到的服务
当while循环不断调用ExecuteOneCommand函数时,将按照trigger表的顺序,依次取出action链表中与trigger匹配的action。 每次均执行一个action中的一个command对应函数(一个action可能携带多个command)。
当一个action所有的command均执行完毕后,再执行下一个action。 当一个trigger对应的action均执行完毕后,再执行下一个trigger对应action。
*/
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;
}
以上代码便是init的大致工作内容,不过学习知识要抓住重点,从本文要分析的两个知识点来看,可以
将init的工作流程精简为以下四点:
工作1 解析配置文件 init.rc
工作2 执行各个阶段的动作,创建zygote的工作就是在其中的某个阶段完成的
工作3 调用 property_init初始化属性相关的资源,并且启动属性服务
工作4 init进入一个无限循环,并且等待一些事情的发生,重点关注init如何处理来自socket和来自属性服务器的相关事情。
精简学习流程,在分析代码的时候很有用!!
二 工作1 :解析配置文件 init.rc
解析配置文件 init.rc
-
1 分别使用 AddSectionParser() 函数创建出Parser(解析器),ServiceParser、ActionParser、ImportParser分别对应 service action import,然后将它们放到一个map里存起来
-
2 使用 ParseConfig()函数解析 init.rc 配置文件
2.1 创建解析器
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
AddSectionParser()方法是现在 system\core\init\init_parser.cpp 中
/*
首先调用 解析器Parser类的成员函数 AddSectionParser,在 map模板类对象 section_parsers_ 中添加键值对,即添加
service action import 分别对应 ServiceParser、ActionParser、ImportParser
这样就定义了一个用service、action、import作为关键字 检索ServiceParser、ActionParser、ImportParser条目的
map对象
*/
void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
section_parsers_[name] = std::move(parser);
}
section_parsers_ 对象定义在 system\core\init\init_parser.h 中
/*
SectionParser 是一个抽象类,有四个纯虚函数,分别是ParseSection、ParseLineSection、EndSection,EndFile.
*/
class SectionParser {
public:
virtual ~SectionParser() {
}
virtual bool ParseSection(const std::vector<std::string>& args,
std::string* err) = 0;
virtual bool ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const = 0;
virtual void EndSection() = 0;
virtual void EndFile(const std::string& filename) = 0;
};
class Parser {
public:
static Parser& GetInstance();
void DumpState() const;
bool ParseConfig(const std::string& path);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
private:
Parser();
void ParseData(const std::string& filename, const std::string& data);
bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
/*
Parser 类包含一个成员变量,即 section_parsers_ ,是用于负责解析语法,在初始化的时候添加三个解析类:
ServiceParser、ActionParser、ImportParser。通过 AddSectionParser()方法添加。
创建一个 map 对象 section_parsers_ 模板类,需要关键字和存储对象两个模板参数
*/
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
};
2.2 解析 init.rc 配置文件
system\core\init\init_parser.cpp
/*
首先是判断传入的是目录还是文件,其实他们都是调用ParseConfigFile,ParseConfigDir就是遍历下该目录中的文件,对文件排个序,然后调用ParseConfigFile.
*/
bool Parser::ParseConfig(const std::string& path) {
if (is_dir(path.c_str())) {
//传入参数为目录地址
return ParseConfigDir(path);
}
//传入参数为文件地址
return ParseConfigFile(path);
}
而 ParseConfigFile 就是读取文件中的数据后, 将数据传递给 ParseData 函数, 最后遍历 section_parsers_ 调用其EndFile函数,
ParseConfigDir
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;
std::vector<std::string> files;
while ((current_file = readdir(config_dir.get()))) {
// Ignore directories and only process regular files.
if (current_file->d_type == DT_REG) {
std::string current_path =
android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
files.emplace_back(current_path);
}
}
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) {
//容易看出,最终仍是调用ParseConfigFile
if (!ParseConfigFile(file)) {
ERROR("Could not import file '%s'\n", file.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'); // TODO: fix parse_config.
//解析获取的字符串
ParseData(path, data);
for (const auto& sp : section_parsers_) {
sp.second->EndFile(path);
}
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
return true;
}
可以看出,ParseConfigFile只是读取文件的内容并转换为字符串。实际的解析工作被交付给ParseData。
system\core\init
//解析 init.rc 转台结构体
struct parse_state
{
char *ptr; //指针,指向剩余的尚未被解析的数据(即:ptr指向当前解析到的位置)
char *text; //一行文本
int line; //行号
int nexttoken; //下一行的标示,T_EOF标示文件结束,T_TEXT表示需要进行解释的文本,T_NEWLINE标示一个空行或者是注释行
void *context; //一个action或者service
void (*parse_line)(struct parse_state *state, int nargs, char **args);//函数指针,指向当前行的解析函数
const char *filename; //解析的rc文件
void *priv; //执行import链表的指针
};
/*
1 ParseData通过调用next_token函数遍历每一个字符,以空格或""为分割将一行拆分成若干个单词,调用T_TEXT将单词放到args数组中。
2 当读到回车符就调用 T_NEWLINE,在 section_parsers_ 这个map中找到对应的 on service import 的解析器,执行 ParseSection 。
3 如果在map中找不到对应的key,就执行 ParseLineSection
4 当读到0的时候,表示一个Section读取结束,调用T_EOF执行EndSection.
*/
void Parser::ParseData(const std::string& filename, const std::string& data) {
//将data的内容复制到data_copy中,并追加一个结束符0
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
//解析用的结构体
/*
parseData 主要是通过 parse_state 控制状态从而解析不同的规则,parse_state是一个结构体,定义在tokenizer.h:
*/
parse_state state;
state.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
//定义了一个对象 args,它的类型是一个string的可变数组。
std::vector<std::string> args;
for (;;) {
//遍历data_copy中每一个字符
switch (next_token(&state)) {
//如果返回为T_EOF,表示init.rc已经解析完成,是文件结尾,调用EndSection
case T_EOF:
if (section_parser) {
section_parser->EndSection();
}
return;
//解析新行
case T_NEWLINE:
state.line++;
if (args.empty()) {
break;
}
/*
init.rc中每一个语法行均是以一个keyword开头的,因此args[0]即表示这一行的keyword,用count()方法确认
section_parsers_映射表中是否存在该keyword ,如果有,返回1;否则,返回0。注意,map中不存在相同元素,所以返回
值只能是1或0
*/
if (section_parsers_.count(args[0])) {
if (section_parser) {
section_parser->EndSection();
}
//得到该行文本中 section_parsers_ 这个map中的args[0],即keyword 对应的 parser,如 on service import等
//
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
//执行 ParseSection,解析对应的 Section
if (!section_parser->ParseSection(args, &ret_err)) {
parse_error(&state, "%s\n", ret_err.c_str());
section_parser = nullptr;
}
//不包含 on service import则是command或option
} else if (section_parser) {
std::string ret_err;
//解析command或option
if (!section_parser->ParseLineSection(args, state.filename,
state.line, &ret_err)) {
parse_error(&state, "%s\n", ret_err.c_str());
}
}
args.clear();
break;
//将读取的一行数据放到args中,args以空格或""作为分割,将一行数据拆分成单词放进数组中
case T_TEXT:
//emplace_back() 是向容器内添加数据.
args.emplace_back(state.text);
break;
}
}
}
问题 : ection_parser->ParseSection(args, &ret_err)是如何工作的?
2.3 ParseSection()解析
section_parser->ParseSection(args, &ret_err)
on service import 三种Parser对应的三个解析器 ActionParser ,ServiceParser,ImportParser
分别分析这三个 解析器 的
ParseSection
ParseLineSection
EndSection
EndFile
ParseSection 如下:
ServiceParser::ParseSection()
ActionParser::ParseSection()
ImportParser::ParseSection()
2.3.1 ActionParser::ParseSection() 即 对应 on 关键字
ServiceParser::ParseSection()分析
system\core\init\action.cpp
bool ActionParser::ParseSection(const std::vector<std::string>& args,std::string* err) {
//创建 triggers 对象,并且把 args 对象中的信息 从 拷贝到 triggers对象
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
*err = "actions must have a trigger";
return false;
}
//创建 action
//auto:用来声明自动变量。它是存储类型标识符
auto action = std::make_unique<Action>(false);
//调用InitTriggers解析trigger
if (!action->InitTriggers(triggers, err)) {
return false;
}
action_ = std::move(action);
return true;
}
/*
InitTriggers 通过比较是否以"property:"开头,区分 trigger 的类型
如果是property trigger,就调用 ParsePropertyTrigger,
如果是 event trigger,就将args的参数赋值给 event_trigger_ ,类型是string
*/
bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
if (i % 2) {
if (args[i] != "&&") {
*err = "&& is the only symbol allowed to concatenate actions";
return false;
} else {
continue;
}
}
//通过比较是否以"property:"开头,区分 trigger 的类型
//trigger 的类型
if (!args[i].compare(0, prop_str.length(), prop_str)) {
if (!ParsePropertyTrigger(args[i], err)) {
return false;
}
}
//event trigger
else {
if (!event_trigger_.empty()) {
*err = "multiple event triggers are not allowed";
return false;
}
event_trigger_ = args[i];
}
}
/*
ParsePropertyTrigger函数先是将字符以"="分割为name-value,然后将name-value存入 property_triggers_ 这个map中
*/
bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
const static std::string prop_str("property:");
//截取 property:后的内容
//获得字符串s中 从第prop_str.length()位开始直到结束的所有字符 保存在 prop_name 中。
std::string prop_name(trigger.substr(prop_str.length()));
//在字符串 prop_name 中查找 '=' 字符 并返回位置
size_t equal_pos = prop_name.find('=');
//如果 字符串 prop_name 中不存在 '='
if (equal_pos == std::string::npos) {
*err = "property trigger found without matching '='";
return false;
}
//从字符串prop_name中查找 '=' 之后的字符,并取出 保存在 prop_value 中
std::string prop_value(prop_name.substr(equal_pos + 1));
//删除prop_name中 包括 equal_pos 的字符,也就是"=" 以及之后的所有字符
prop_name.erase(equal_pos);
// 将 prop_name 与 prop_value 键值对 插入到 property_triggers_ 中
auto res = property_triggers_.emplace(prop_name, prop_value);
if (res.second == false) {
*err = "multiple property triggers found for same property";
return false;
}
return true;
}
return true;
}
从上面看出,ParseSection 函数的作用就是构造一个 Action 对象,将trigger条件记录到Action这个对象中,如果是event trigger就赋值给event_trigger_,如果是property trigger就存放到property_triggers_这个map中。
2.3.2 ServiceParser::ParseSection()即 对应 service 关键字
system\core\init\service.cpp
bool ServiceParser::ParseSection(const std::vector<std::string>& args, std::string* err) {
if (args.size() < 3) {
*err = "services must have a name and a program";
return false;
}
const std::string& name = args[1];
if (!IsValidName(name)) {
*err = StringPrintf("invalid service name '%s'", name.c_str());
return false;
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, "default", str_args);
return true;
}
待续。。。