Android系统之init简析

本文深入剖析Android系统中init进程的启动流程与关键职责,包括创建核心进程如zygote,初始化属性服务,以及解析init.rc配置文件的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文代码环境为 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;
}

待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值