Android Framework学习之init进程解析

业无高卑志当坚,男儿有求安得闲?这篇文章主要讲述Android Framework学习之init进程解析相关的知识,希望能为你提供帮助。
init进程是android系统中用户空间的第一个进程, 它被赋予了很多极其重要的工作职责, init进程相关源码位于system/core/init, 本篇博客我们就一起来学习init进程( 基于Android 7.0) 。
init入口函数分析 init的入口函数为main, 位于system/core/init/init.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); }// Clear the umask. umask(0); add_environment(" PATH" , _PATH_DEFPATH); bool is_first_stage = (argc = = 1) || (strcmp(argv[1], " --second-stage" ) != 0); // Get the basic filesystem setup we need put together in the initramdisk // on / and then we' ll let the rc file figure out the rest. //1.创建一些文件夹, 并挂载设备, 这些都是与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); }// We must have some place other than / to create the device nodes for // kmsg and null, otherwise we won' t be able to remount / read-only // later on. Now that tmpfs is mounted on /dev, we can actually talk // to the outside world. //2.重定向标准输入, 输出, 错误输出到/dev/_null_ open_devnull_stdio(); 3.初始化内核log系统 klog_init(); klog_set_level(KLOG_NOTICE_LEVEL); 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)); //4.初始化和属性相关资源 property_init(); // If arguments are passed both on the command line and in DT, // properties set in DT always have priority over the command-line ones. process_kernel_dt(); process_kernel_cmdline(); // Propagate the kernel variables to internal variables // used by init as well as the current required properties. export_kernel_boot_props(); }// Set up SELinux, including loading the SELinux policy if we' re in the kernel domain. 5.完成SELinux相关工作 selinux_initialize(is_first_stage); // If we' re in the kernel domain, re-exec init to transition to the init domain now // that the SELinux policy has been loaded. if (is_first_stage) { 6.重新设置属性 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(); } }// These directories were necessarily created before initial policy load // and therefore need their security context restored to the proper value. // This must happen before /dev is populated by ueventd. NOTICE(" Running restorecon...\\n" ); restorecon(" /dev" ); restorecon(" /dev/socket" ); restorecon(" /dev/__properties__" ); restorecon(" /property_contexts" ); restorecon_recursive(" /sys" ); 7.创建epoll句柄 epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd = = -1) { ERROR(" epoll_create1 failed: %s\\n" , strerror(errno)); exit(1); } 8.装载子进程信号处理器 signal_handler_init(); property_load_boot_defaults(); export_oem_lock_status(); //9.启动属性服务 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> ()); //10.解析init.rc配置文件 parser.ParseConfig(" /init.rc" ); ActionManager& am = ActionManager::GetInstance(); am.QueueEventTrigger(" early-init" ); // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev... am.QueueBuiltinAction(wait_for_coldboot_done_action, " wait_for_coldboot_done" ); // ... so that we can start queuing up actions that require stuff from /dev. 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" ); // Trigger all the boot actions to get us started. am.QueueEventTrigger(" init" ); // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random // wasn' t ready immediately after wait_for_coldboot_done am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, " mix_hwrng_into_linux_rng" ); // Don' t mount filesystems or start core system services in charger mode. std::string bootmode = property_get(" ro.bootmode" ); if (bootmode = = " charger" ) { am.QueueEventTrigger(" charger" ); } else { am.QueueEventTrigger(" late-init" ); }// Run all property triggers based on current state of the properties. 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; }

从上面代码中可以精简归纳init的main方法做的事情:
1.创建文件系统目录并挂载相关的文件系统
2.屏蔽标准的输入输出
3.初始化内核log系统
4.调用property_init初始化属性相关的资源
5.完成SELinux相关工作
6.重新设置属性
7.创建epoll句柄
8.装载子进程信号处理器
9.通过property_start_service启动属性服务
10.通过parser.ParseConfig(“/init.rc”)来解析init.rc
接下来对上述部分步骤, 进行详细解析。
1.创建文件系统目录并挂载相关的文件系统
//清除屏蔽字(file mode creation mask), 保证新建的目录的访问权限不受屏蔽字影响。 umask(0); add_environment(" PATH" , _PATH_DEFPATH); bool is_first_stage = (argc = = 1) || (strcmp(argv[1], " --second-stage" ) != 0); // Get the basic filesystem setup we need put together in the initramdisk 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系统源码时, 在生成的根文件系统中, 并不存在这些目录, 它们是系统运行时的目录, 即当系统终止时, 就会消失。
在init初始化过程中, Android分别挂载了tmpfs, devpts, proc, sysfs这4类文件系统。
2.屏蔽标准的输入输出
open_devnull_stdio();

前文生成/dev目录后, init进程将调用open_devnull_stdio函数, 屏蔽标准的输入输出。
open_devnull_stdio函数会在/dev目录下生成null设备节点文件, 并将标准输入、标准输出、标准错误输出全部重定向到null设备中。
void open_devnull_stdio(void) { // Try to avoid the mknod() call if we can. Since SELinux makes // a /dev/null replacement available for free, let' s use it. int fd = open(" /sys/fs/selinux/null" , O_RDWR); if (fd = = -1) { // OOPS, /sys/fs/selinux/null isn' t available, likely because // /sys/fs/selinux isn' t mounted. Fall back to mknod. static const char *name = " /dev/__null__" ; if (mknod(name, S_IFCHR | 0600, (1 < < 8) | 3) = = 0) { fd = open(name, O_RDWR); unlink(name); } if (fd = = -1) { exit(1); } }dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) { close(fd); } }

open_devnull_stdio函数定义于system/core/init/util.cpp中。
这里需要说明的是, dup2函数的作用是用来复制一个文件的描述符, 通常用来重定向进程的stdin、stdout和stderr。它的函数原形是:
int dup2(int oldfd, int targetfd)
该函数执行后, targetfd将变成oldfd的复制品。
因此上述过程其实就是: 创建出null设备后, 将0、1、2绑定到null设备上。因此init进程调用open_devnull_stdio函数后, 通过标准的输入输出无法输出信息。
4.初始化属性域
if (!is_first_stage) { ....... property_init(); ....... }

调用property_init初始化属性域。在Android平台中, 为了让运行中的所有进程共享系统运行时所需要的各种设置值, 系统开辟了属性存储区域, 并提供了访问该区域的API。
需要强调的是, 在init进程中有部分代码块以is_first_stage标志进行区分, 决定是否需要进行初始化, 而is_first_stage的值, 由init进程main函数的入口参数决定。 其原因在于, 在引入selinux机制后, 有些操作必须要在内核态才能完成;
但init进程作为android的第一个进程, 又是运行在用户态的。
于是, 最终设计为用is_first_stage进行区分init进程的运行状态。init进程在运行的过程中, 会完成从内核态到用户态的切换。
void property_init() { if (__system_property_area_init()) { ERROR(" Failed to initialize property area\\n" ); exit(1); } }

property_init函数定义于system/core/init/property_service.cpp中, 如上面代码所示, 最终调用_system_property_area_init函数初始化属性域。
5.完成SELinux相关工作
// Set up SELinux, including loading the SELinux policy if we' re in the kernel domain. selinux_initialize(is_first_stage);

init进程进程调用selinux_initialize启动SELinux。从注释来看, init进程的运行确实是区分用户态和内核态的。
static void selinux_initialize(bool in_kernel_domain) { Timer t; selinux_callback cb; //用于打印log的回调函数 cb.func_log = selinux_klog_callback; selinux_set_callback(SELINUX_CB_LOG, cb); //用于检查权限的回调函数 cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); if (in_kernel_domain) { //内核态处理流程 INFO(" Loading SELinux policy...\\n" ); //用于加载sepolicy文件。该函数最终将sepolicy文件传递给kernel, 这样kernel就有了安全策略配置文件, 后续的MAC才能开展起来。 if (selinux_android_load_policy() < 0) { ERROR(" failed to load policy: %s\\n" , strerror(errno)); security_failure(); } //内核中读取的信息 bool kernel_enforcing = (security_getenforce() = = 1); //命令行中得到的数据 bool is_enforcing = selinux_is_enforcing(); if (kernel_enforcing != is_enforcing) { //用于设置selinux的工作模式。selinux有两种工作模式: //1、”permissive”, 所有的操作都被允许( 即没有MAC) , 但是如果违反权限的话, 会记录日志 //2、”enforcing”, 所有操作都会进行权限检查。在一般的终端中, 应该工作于enforing模式 if(security_setenforce(is_enforcing)) { ........ //将重启进入recovery mode security_failure(); } } if (write_file(" /sys/fs/selinux/checkreqprot" , " 0" ) = = -1) { security_failure(); }NOTICE(" (Initializing SELinux %s took %.2fs.)\\n" , is_enforcing ? " enforcing" : " non-enforcing" , t.duration()); } else { selinux_init_all_handles(); } }

6.重新设置属性
// If we' re in the kernel domain, re-exec init to transition to the init domain now that the SELinux policy has been loaded. if (is_first_stage) { //按selinux policy要求, 重新设置init文件属性 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 }; //这里就是前面所说的, 启动用户态的init进程, 即second-stage if (execv(path, args) = = -1) { ERROR(" execv(\\" %s\\" ) failed: %s\\n" , path, strerror(errno)); security_failure(); } }// These directories were necessarily created before initial policy load // and therefore need their security context restored to the proper value. // This must happen before /dev is populated by ueventd. INFO(" Running restorecon...\\n" ); restorecon(" /dev" ); restorecon(" /dev/socket" ); restorecon(" /dev/__properties__" ); restorecon_recursive(" /sys" );

上述文件节点在加载Sepolicy之前已经被创建了, 因此在加载完Sepolicy后, 需要重新设置相关的属性。
9.启动配置属性的服务端
start_property_service();

init进程在共享内存区域中, 创建并初始化属性域。其它进程可以访问属性域中的值, 但更改属性值仅能在init进程中进行。这就是init进程调用start_property_service的原因。其它进程修改属性值时, 要预先向init进程提交值变更申请, 然后init进程处理该申请, 并修改属性值。在访问和修改属性时, init进程都可以进行权限控制。
void start_property_service() { //创建了一个非阻塞socket property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0666, 0, 0, NULL); if (property_set_fd = = -1) { ERROR(" start_property_service socket creation failed: %s\\n" , strerror(errno)); exit(1); } //调用listen函数监听property_set_fd, 于是该socket变成一个server listen(property_set_fd, 8); //监听server socket上是否有数据到来 register_epoll_handler(property_set_fd,handle_property_set_fd); }

我们知道, 在create_socket函数返回套接字property_set_fd时, property_set_fd是一个主动连接的套接字。此时, 系统假设用户会对这个套接字调用connect函数, 期待它主动与其它进程连接。
由于在服务器编程中, 用户希望这个套接字可以接受外来的连接请求, 也就是被动等待用户来连接, 于是需要调用listen函数使用主动连接套接字变为被连接套接字, 使得一个进程可以接受其它进程的请求, 从而成为一个服务器进程。
因此, 调用listen后, init进程成为一个服务进程, 其它进程可以通过property_set_fd连接init进程, 提交设置系统属性的申请。
listen函数的第二个参数, 涉及到一些网络的细节。
在进程处理一个连接请求的时候, 可能还存在其它的连接请求。因为TCP连接是一个过程, 所以可能存在一种半连接的状态。有时由于同时尝试连接的用户过多, 使得服务器进程无法快速地完成连接请求。
因此, 内核会在自己的进程空间里维护一个队列, 以跟踪那些已完成连接但服务器进程还没有接手处理的用户, 或正在进行的连接的用户。这样的一个队列不可能任意大, 所以必须有一个上限。listen的第二个参数就是告诉内核使用这个数值作为上限。因此, init进程作为系统属性设置的服务器, 最多可以同时为8个试图设置属性的用户提供服务。
在启动配置属性服务的最后, 调用函数register_epoll_handler。该函数将利用之前创建出的epoll句柄监听property_set_fd。当property_set_fd中有数据到来时, init进程将利用handle_property_set_fd函数进行处理。
static void handle_property_set_fd() { .......... if ((s = accept(property_set_fd, (struct sockaddr *) & addr, & addr_size)) < 0) { return; } ........ r = TEMP_FAILURE_RETRY(recv(s, & msg, sizeof(msg), MSG_DONTWAIT)); ......... switch(msg.cmd) { ......... } ......... }

handle_propery_set_fd函数实际上是调用accept函数监听连接请求, 接收property_set_fd中到来的数据, 然后利用recv函数接受到来的数据, 最后根据到来数据的类型, 进行设置系统属性等相关操作, 在此不做深入分析。
介绍一下系统属性改变的一些用途。
在init.rc中定义了一些与属性相关的触发器。当某个条件相关的属性被改变时, 与该条件相关的触发器就会被触发。举例来说, 如下面代码所示, debuggable属性变为1时, 将执行启动console进程等操作。
on property:ro.debuggable= 1 # Give writes to anyone for the trace folder on debug builds. # The folder is used to store method traces. chmod 0773 /data/misc/trace start console

总结一下, 其它进程修改系统属性时, 大致的流程如下图所示: 其它的进程像init进程发送请求后, 由init进程检查权限后, 修改共享内存区。
Android Framework学习之init进程解析

文章图片

10.解析配置文件init.rc init.rc是系统配置文件, 位于system/core/rootdir/init.rc, Android 7.0中对init.rc文件进行了拆分, 每个服务一个rc文件。
init.rc文件是在init进程启动后执行的启动脚本, 文件中记录着init进程需执行的操作。在Android系统中, 使用init.rc和init.{ hardware }.rc两个文件。
其中init.rc文件在Android系统运行过程中用于通用的环境设置与进程相关的定义, init.{hardware}.rc( 例如, 高通有init.qcom.rc, MTK有init.mediatek.rc) 用于定义Android在不同平台下的特定进程和环境设置等。
init.rc文件大致分为两大部分, 一部分是以“on”关键字开头的动作列表( action list) :
on early-init # Set init and its forked children' s oom_adj. write /proc/1/oom_score_adj -1000 ......... start ueventd

另一部分是以“service”关键字开头的服务列表( service list) :
service ueventd /sbin/ueventd class core critical seclabel u:r:ueventd:s0

动作列表用于创建所需目录, 以及为某些特定文件指定权限, 而服务列表用来记录init进程需要启动的一些子进程。如上面代码所示, service关键字后的第一个字符串表示服务( 子进程) 的名称, 第二个字符串表示服务的执行路径。
接下来, 我们从ParseConfig函数入手, 逐步分析整个解析过程(函数定义于system/core/init/ Init_parser.cpp中):
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) { ........... std::unique_ptr< DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir); .......... //看起来很复杂, 其实就是递归目录 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) { //最终还是靠ParseConfigFile来解析实际的文件 if (!ParseConfigFile(current_path)) { ............. } } } }

从上面的代码可以看出, 解析init.rc文件的函数是ParseConfigFile:
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; }

ParseData函数定义于system/core/init/init_parser.cpp中, 根据关键字解析出服务和动作。动作与服务会以链表节点的形式注册到service_list与action_list中, service_list与action_list是init进程中声明的全局结构体
void Parser::ParseData(const std::string& filename, const std::string& data) { ....... parse_state state; ....... std::vector< std::string> args; for (; ; ) { //next_token以行为单位分割参数传递过来的字符串 //最先走到T_TEXT分支 switch (next_token(& state)) { case T_EOF: if (section_parser) { //EOF,解析结束 section_parser-> EndSection(); } return; case T_NEWLINE: state.line+ + ; if (args.empty()) { break; } //创建parser时, 会为init.rc中以service, on, import开头的都定义了对应的解析parser //这里就是根据第一个参数, 判断是否有对应的parser if (section_parsers_.count(args[0])) { if (section_parser) { //结束上一个parser的工作, 将构造出的对象加入到对应的service_list与action_list中 section_parser-> EndSection(); } //获取参数对应的parser section_parser = section_parsers_[args[0]].get(); std::string ret_err; //调用实际parser的ParseSection函数 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; //如果第一个参数不是service, on, import //则调用前一个parser的ParseLineSection函数 //这里相当于解析一个参数块的子项 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中 args.emplace_back(state.text); break; } } }

这里的解析看起来比较复杂, 在6.0以前的版本中, 整个解析是面向过程的。init进程统一调用一个函数来进行解析, 然后在该函数中利用switch-case的形式, 根据解析的内容进行相应的处理。
在Android 7.0中, 为了更好地封装及面向对象, 对于不同的关键字定义了不同的parser对象, 每个对象通过多态实现自己的解析操作。
在init进程main函数中, 创建各种parser的代码如下:
........... 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的定义:
class ServiceParser : public SectionParser {......} class ActionParser : public SectionParser {......} class ImportParser : public SectionParser {.......}

可以看到三个Parser均是继承SectionParser, 具体的实现各有不同, 我们以比较常用的ServiceParser和ActionParser为例
ServiceParser
ServiceParser定义于system/core/init/service.cpp中。从前面的代码, 我们知道, 解析一个service块, 首先需要调用ParseSection函数, 接着利用ParseLineSection处理子块, 解析完所有数据后, 调用EndSection。
因此, 我们着重看看ServiceParser的这三个函数:
bool ServiceParser::ParseSection(.....) { ....... const std::string& name = args[1]; ....... std::vector< std::string> str_args(args.begin() + 2, args.end()); //主要根据参数, 构造出一个service对象 service_ = std::make_unique< Service> (name, " default" , str_args); return true; }

//注意这里已经在解析子项了 bool ServiceParser::ParseLineSection(......) const { //调用service对象的HandleLine return service_ ? service_-> HandleLine(args, err) : false; }

bool Service::HandleLine(.....) { ........ //OptionHandlerMap继承自keywordMap< OptionHandler> static const OptionHandlerMap handler_map; //根据子项的内容, 找到对应的handler函数 //FindFunction定义于keyword模块中,FindFunction方法利用子类生成对应的map中, 然后通过通用的查找方法, 即比较键值找到对应的处理函数 auto handler = handler_map.FindFunction(args[0], args.size() - 1, err); if (!handler) { return false; } //调用handler函数 return (this-> *handler)(args, err); }

class Service::OptionHandlerMap : public KeywordMap< OptionHandler> { ........... Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const { constexpr std::size_t kMax = std::numeric_limits< std::size_t> ::max(); static const Map option_handlers = { {" class" ,{1,1,& Service::HandleClass}}, {" console" ,{0,0,& Service::HandleConsole}}, {" critical" ,{0,0,& Service::HandleCritical}}, {" disabled" ,{0,0,& Service::HandleDisabled}}, {" group" ,{1,NR_SVC_SUPP_GIDS + 1, & Service::HandleGroup}}, {" ioprio" ,{2,2,& Service::HandleIoprio}}, {" keycodes" ,{1,kMax, & Service::HandleKeycodes}}, {" oneshot" ,{0,0,& Service::HandleOneshot}}, {" onrestart" ,{1,kMax, & Service::HandleOnrestart}}, {" seclabel" ,{1,1,& Service::HandleSeclabel}}, {" setenv" ,{2,2,& Service::HandleSetenv}}, {" socket" ,{3,6,& Service::HandleSocket}}, {" user" ,{1,1,& Service::HandleUser}}, {" writepid" ,{1,kMax, & Service::HandleWritepid}}, }; return option_handlers; }

//以class对应的处理函数为例, 可以看出其实就是填充service对象对应的域 bool Service::HandleClass(const std::vector< std::string> & args, std::string* err) { classname_ = args[1]; return true; }//注意此时service对象已经处理完毕 void ServiceParser::EndSection() { if (service_) { ServiceManager::GetInstance().AddService(std::move(service_)); } }void ServiceManager::AddService(std::unique_ptr< Service> service) { Service* old_service = FindServiceByName(service-> name()); if (old_service) { ERROR(" ignored duplicate definition of service ' %s' " , service-> name().c_str()); return; } //将service对象加入到services_里 //7.0里, services_已经是个vector了 services_.emplace_back(std::move(service)); }

总结一下: ServiceParser中, 首先根据第一行的名字和参数创建出service对象, 然后根据选项域的内容填充service对象, 最后将创建出的service对象加入到vector类型的service链表中。
【Android Framework学习之init进程解析】ActionParser
ActionParser定义于system/core/init/action.cpp中。Action的解析过程, 其实与Service一样, 也是先后调用ParseSection, ParseLineSection和EndSection。
bool ActionParser::ParseSection(....) { ........ //创建出新的action对象 auto action = std::make_unique< Action> (false); //根据参数, 填充action的trigger域, 不详细分析了 if (!action-> InitTriggers(triggers, err)) { return false; } ......... }bool ActionParser::ParseLineSection(.......) const { //构造Action对象的command域 return action_ ? action_-> AddCommand(args, filename, line, err) : false; }bool Action::AddCommand(.....) { ........ //找出action对应的执行函数 auto function = function_map_-> FindFunction(args[0], args.size() - 1, err); ........ //利用所有信息构造出command, 加入到action对象中 AddCommand(function, args, filename, line); return true; }void Action::AddCommand(......) { commands_.emplace_back(f, args, filename, line); }void ActionParser::EndSection() { if (action_ & & action_-> NumCommands() > 0) { ActionManager::GetInstance().AddAction(std::move(action_)); } }void ActionManager::AddAction(.....) { ........ auto old_action_it = std::find_if(actions_.begin(), actions_.end(), [& action] (std::unique_ptr< Action> & a) { return action-> TriggersEqual(*a); }); if (old_action_it != actions_.end()) { (*old_action_it)-> CombineAction(*action); } else { //加入到action链表中, 类型也是vector, 其中装的是指针 actions_.emplace_back(std::move(action)); } }

可以看出, 加载action块的逻辑和service一样, 不同的是需要填充trigger和command域。当然, 最后解析出的action也需要加入到action链表中。
这里最后还剩下一个问题, 那就是哪里定义了Action中command对应处理函数?
答案就是在init.cpp的main函数中:
....... const BuiltinFunctionMap function_map; Action::set_function_map(& function_map); .......

Action中调用function_map_-> FindFunction时, 实际上调用的是BuiltinFunctionMap的FindFunction函数。FindFunction是keyword定义的通用函数, 重点是重构的map函数。所以需要看BuiltinFunctionMap, 其定义在system/core/init/builtins.cpp:
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_pe

    推荐阅读