Android启动篇 — init原理(一)中讲解分init进程分析init创建系统目录并挂在相应系统文件、初始化属性域、设置系统属性、启动配置属性服务端等一系列复杂工作,很多工作和知识点跟Linux关系很大,所以没有作过多介绍,而本此对于init.rc的解析则是重中之重,所以单独拿出来进行详细分析。

Java代码
  1. int main(int argc, char** argv) {  
  2.     /* 01. 创建文件系统目录并挂载相关的文件系统 */  
  3.     /* 02. 屏蔽标准的输入输出/初始化内核log系统 */  
  4.     /* 03. 初始化属性域 */  
  5.     /* 04. 完成SELinux相关工作 */•  
  6.     /* 05. 重新设置属性 */  
  7.     /* 06. 创建epoll句柄 */  
  8.     /* 07. 装载子进程信号处理器 */  
  9.     /* 08. 设置默认系统属性 */  
  10.     /* 09. 启动配置属性的服务端 */  
  11.     /* 10. 匹配命令和函数之间的对应关系 */  
  12. -------------------------------------------------------------------------------------------   // Android启动篇 — init原理(一)中讲解  
  13.     /* 11. 解析init.rc */  
  14.     Parser& parser = Parser::GetInstance();       // 构造解析文件用的parser对象  
  15.     // 增加ServiceParser为一个section,对应name为service  
  16.     parser.AddSectionParser("service",std::make_unique<ServiceParser>());  
  17.     // 增加ActionParser为一个section,对应name为action  
  18.     parser.AddSectionParser("on", std::make_unique<ActionParser>());  
  19.     // 增加ImportParser为一个section,对应name为service  
  20.     parser.AddSectionParser("import", std::make_unique<ImportParser>());  
  21.     parser.ParseConfig("/init.rc");      // 开始实际的解析过程  

  【正文】

  init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,主要包含五种类型语句:Action、Command、Service、Option和Import,在分析代码的过程中我们会详细介绍。

  init.rc的配置代码在:system/core/rootdir/init.rc 中

  init.rc文件是在init进程启动后执行的启动脚本,文件中记录着init进程需执行的操作。

  init.rc文件大致分为两大部分,一部分是以“on”关键字开头的动作列表(action list):

XML/HTML代码
  1. on early-init      // Action类型语句  
  2.     # Set init and its forked children's oom_adj.     // #:注释符号  
  3.     write /proc/1/oom_score_adj -1000  
  4.     ... ...  
  5.     start ueventd  

  Action类型语句格式:

XML/HTML代码
  1. on <trigger> [&& <trigger>]*     // 设置触发器    
  2.    <command>    
  3.    <command>      // 动作触发之后要执行的命令  

  另一部分是以“service”关键字开头的服务列表(service list):  如 Zygote

XML/HTML代码
  1. service ueventd /sbin/ueventd  
  2.     class core  
  3.     critical  
  4.     seclabel u:r:ueventd:s0  

  Service类型语句格式:

XML/HTML代码
  1. service <name> <pathname> [ <argument> ]*   // <service的名字><执行程序路径><传递参数>    
  2.    <option>       // option是service的修饰词,影响什么时候、如何启动services    
  3.    <option>    
  4.    ...  

  借助系统环境变量或Linux命令,动作列表用于创建所需目录,以及为某些特定文件指定权限,而服务列表用来记录init进程需要启动的一些子进程。如上面代码所示,service关键字后的第一个字符串表示服务(子进程)的名称,第二个字符串表示服务的执行路径。

  值得一提的是在Android 7.0中对init.rc文件进行了拆分,每个服务一个rc文件。我们要分析的zygote服务的启动脚本则在init.zygoteXX.rc中定义。

  在init.rc的import段我们看到如下代码:

XML/HTML代码
  1. import /init.${ro.zygote}.rc     // 可以看出init.rc不再直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件  

  说明:

  从android5.0开始,android开始支持64位的编译,zygote本身也就有了32位和64位的区别,所以在这里用ro.zygote属性来控制启动不同版本的zygote进程。

  init.rc位于/system/core/rootdir下。在这个路径下还包括四个关于zygote的rc文件。分别是Init.zygote32.rc,Init.zygote32_64.rc,Init.zygote64.rc,Init.zygote64_32.rc,由硬件决定调用哪个文件。

  这里拿32位处理器为例,init.zygote32.rc的代码如下所示:

XML/HTML代码
  1. service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server  
  2.     class main         # class是一个option,指定zygote服务的类型为main  
  3.     socket zygote stream 660 root system          # socket关键字表示一个option,创建一个名为dev/socket/zygote,类型为stream,权限为660的socket  
  4.     onrestart write /sys/android_power/request_state wake          # onrestart是一个option,说明在zygote重启时需要执行的command  
  5.     onrestart write /sys/power/state on  
  6.     onrestart restart audioserver  
  7.     onrestart restart cameraserver  
  8.     onrestart restart media  
  9.     onrestart restart netd  
  10.     writepid /dev/cpuset/foreground/tasks  

  “service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server”

  在Init.zygote32.rc中,定义了一个zygote服务:zygote,由关键字service告诉init进程创建一个名为zygote的进程,这个进程要执行的程序是:/system/bin/app_process,给这个进程四个参数:

  · -Xzygote:该参数将作为虚拟机启动时所需的参数

  · /system/bin:代表虚拟机程序所在目录

  · --zygote:指明以ZygoteInit.java类中的main函数作为虚拟机执行入口

  · --start-system-server:告诉Zygote进程启动SystemServer进程

  接下来,我们回到源码当中,继续分析main函数:

Java代码
  1. /* 11. 解析init.rc */  
  2. Parser& parser = Parser::GetInstance();       // 构造解析文件用的parser对象  
  3. // 增加ServiceParser为一个section,对应name为service  
  4. parser.AddSectionParser("service",std::make_unique<ServiceParser>());  
  5. // 增加ActionParser为一个section,对应name为action  
  6. parser.AddSectionParser("on", std::make_unique<ActionParser>());  
  7. // 增加ImportParser为一个section,对应name为service  
  8. parser.AddSectionParser("import", std::make_unique<ImportParser>());  
  9. parser.ParseConfig("/init.rc");      // 开始实际的解析过程

  说明:

  上面在解析init.rc文件时使用了Parser类(在init目录下的init_parser.h中定义), 初始化ServiceParser用来解析 “service”块,ActionParser用来解析"on"块,ImportParser用来解析“import”块,“import”是用来引入一个init配置文件,来扩展当前配置的。

  /system/core/init/readme.txt 中对init文件中的所有关键字做了介绍,主要包含了Actions, Commands, Services, Options, and Imports等,可自行学习解读。

  分析init.rc的解析过程:函数定义于system/core/init/ init_parser.cpp中

Java代码
  1. bool Parser::ParseConfig(const std::string& path) {  
  2.     if (is_dir(path.c_str())) {           // 判断传入参数是否为目录地址  
  3.         return ParseConfigDir(path);      // 递归目录,最终还是靠ParseConfigFile来解析实际的文件  
  4.     }  
  5.     return ParseConfigFile(path);         // 传入传输为文件地址  
  6. }  

  继续分析ParseConfigFile():

Java代码
  1. bool Parser::ParseConfigFile(const std::string& path) {  
  2.     ... ...  
  3.     Timer t;  
  4.     std::string data;  
  5.     if (!read_file(path.c_str(), &data)) {       // 读取路径指定文件中的内容,保存为字符串形式  
  6.         return false;  
  7. }  
  8. ... ...  
  9.     ParseData(path, data);        // 解析获取的字符串  
  10.     ... ...  
  11. }  

  跟踪ParseData():

Java代码
  1. void Parser::ParseData(const std::string& filename, const std::string& data) {  
  2.     ... ...  
  3.     parse_state state;  
  4.     ... ...  
  5.     std::vector<std::string> args;  
  6.   
  7.     for (;;) {  
  8.         switch (next_token(&state)) {    // next_token以行为单位分割参数传递过来的字符串,最先走到T_TEXT分支  
  9.         case T_EOF:  
  10.             if (section_parser) {  
  11.                 section_parser->EndSection();    // 解析结束  
  12.             }  
  13.             return;  
  14.         case T_NEWLINE:  
  15.             state.line++;  
  16.             if (args.empty()) {  
  17.                 break;  
  18.             }  
  19.             // 在前文创建parser时,我们为service,on,import定义了对应的parser   
  20.             // 这里就是根据第一个参数,判断是否有对应的parser  
  21.             if (section_parsers_.count(args[0])) {  
  22.                 if (section_parser) {  
  23.                     // 结束上一个parser的工作,将构造出的对象加入到对应的service_list与action_list中  
  24.                     section_parser->EndSection();  
  25.                 }  
  26.                 // 获取参数对应的parser  
  27.                 section_parser = section_parsers_[args[0]].get();  
  28.                 std::string ret_err;  
  29.                 // 调用实际parser的ParseSection函数  
  30.                 if (!section_parser->ParseSection(args, &ret_err)) {  
  31.                     parse_error(&state, "%s\n", ret_err.c_str());  
  32.                     section_parser = nullptr;  
  33.                 }  
  34.             } else if (section_parser) {  
  35.                 std::string ret_err;  
  36.                 // 如果第一个参数不是service,on,import  
  37.                 // 则调用前一个parser的ParseLineSection函数  
  38.                 // 这里相当于解析一个参数块的子项  
  39.                 if (!section_parser->ParseLineSection(args, state.filename,   
  40.                                                              state.line, &ret_err)) {  
  41.                     parse_error(&state, "%s\n", ret_err.c_str());  
  42.                 }  
  43.             }  
  44.             args.clear();       // 清空本次解析的数据  
  45.             break;  
  46.         case T_TEXT:  
  47.             args.emplace_back(state.text);     //将本次解析的内容写入到args中  
  48.             break;  
  49.         }  
  50.     }  
  51. }  

  至此,init.rc解析完,接下来init会执行几个重要的阶段:

Java代码
  1. int main(int argc, char** argv) {  
  2.     /* 01. 创建文件系统目录并挂载相关的文件系统 */  
  3.     /* 02. 屏蔽标准的输入输出/初始化内核log系统 */  
  4.     /* 03. 初始化属性域 */  
  5.     /* 04. 完成SELinux相关工作 */•  
  6.     /* 05. 重新设置属性 */  
  7.     /* 06. 创建epoll句柄 */  
  8.     /* 07. 装载子进程信号处理器 */  
  9.     /* 08. 设置默认系统属性 */  
  10.     /* 09. 启动配置属性的服务端 */  
  11.     /* 10. 匹配命令和函数之间的对应关系 */  
  12.     /* 11. 解析init.rc*/  
  13. ----------------------------------------------------------------------------  
  14.   /* 12.  向执行队列中添加其他action */  
  15.     // 获取ActionManager对象,需要通过am对命令执行顺序进行控制  
  16.     ActionManager& am = ActionManager::GetInstance();  
  17.     // init执行命令触发器主要分为early-init,init,late-init,boot等  
  18.     am.QueueEventTrigger("early-init");    // 添加触发器early-init,执行on early-init内容  
  19.   
  20.     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...  
  21.     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  22.     // ... so that we can start queuing up actions that require stuff from /dev.  
  23.     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  24.     am.QueueBuiltinAction(keychord_init_action, "keychord_init");  
  25.     am.QueueBuiltinAction(console_init_action, "console_init");  
  26.   
  27.     // Trigger all the boot actions to get us started.  
  28.     am.QueueEventTrigger("init");        // 添加触发器init,执行on init内容,主要包括创建/挂在一些目录,以及symlink等  
  29.   
  30.     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random  
  31.     // wasn't ready immediately after wait_for_coldboot_done  
  32.     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  33.   
  34.     // Don't mount filesystems or start core system services in charger mode.  
  35.     if (bootmode == "charger") {  
  36.     am.QueueEventTrigger("charger");     // on charger阶段  
  37.     } else if (strncmp(bootmode.c_str(), "ffbm"4) == 0) {  
  38.     NOTICE("Booting into ffbm mode\n");  
  39.     am.QueueEventTrigger("ffbm");  
  40.     } else {  
  41.     am.QueueEventTrigger("late-init");          // 非充电模式添加触发器last-init  
  42.     }  
  43.   
  44.     // Run all property triggers based on current state of the properties.  
  45.     am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");  

  在last-init最后阶段有如下代码:

XML/HTML代码
  1. # Mount filesystems and start core system services.  
  2. on late-init  
  3.     trigger early-fs  
  4.   
  5.     # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter  
  6.     # '--early' can be specified to skip entries with 'latemount'.  
  7.     # /system and /vendor must be mounted by the end of the fs stage,  
  8.     # while /data is optional.  
  9.     trigger fs  
  10.     trigger post-fs  
  11.   
  12.     # Load properties from /system/ + /factory after fs mount. Place  
  13.     # this in another action so that the load will be scheduled after the prior  
  14.     # issued fs triggers have completed.  
  15.     trigger load_system_props_action  
  16.   
  17.     # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter  
  18.     # to only mount entries with 'latemount'. This is needed if '--early' is  
  19.     # specified in the previous mount_all command on the fs stage.  
  20.     # With /system mounted and properties form /system + /factory available,  
  21.     # some services can be started.  
  22.     trigger late-fs  
  23.   
  24.     # Now we can mount /data. File encryption requires keymaster to decrypt  
  25.     # /data, which in turn can only be loaded when system properties are present.  
  26.     trigger post-fs-data  
  27.   
  28.     # Load persist properties and override properties (if enabled) from /data.  
  29.     trigger load_persist_props_action  
  30.   
  31.     # Remove a file to wake up anything waiting for firmware.  
  32.     trigger firmware_mounts_complete  
  33.   
  34.     trigger early-boot  
  35.    trigger boot  

  可见出发了on early-boot和on boot两个Action。

  我们看一下on boot:

XML/HTML代码
  1. on boot  
  2.     # basic network init  
  3.     ifup lo  
  4.     hostname localhost  
  5.     domainname localdomain  
  6.     ... ...  
  7.     class_start core  

  在on boot 的最后class_start core 会启动class为core的服务,这些服务包括ueventd、logd、healthd、adbd(disabled)、lmkd(LowMemoryKiller)、servicemanager、vold、debuggerd、surfaceflinger、bootanim(disabled)等。

  回到主题,分析trigger触发器的代码,QueueEventTrigger():位于system/core/init/action.cpp

Java代码
  1. void ActionManager::QueueEventTrigger(const std::string& trigger) {  
  2.     trigger_queue_.push(std::make_unique<EventTrigger>(trigger));  
  3. }  

  此处QueueEventTrigger函数就是利用参数构造EventTrigger,然后加入到trigger_queue_中。后续init进程处理trigger事件时,将会触发相应的操作。

  再看一下QueueBuiltinAction()函数:同样位于system/core/init/action.cpp

Java代码
  1. void ActionManager::QueueBuiltinAction(BuiltinFunction func,  
  2.                                    const std::string& name) {  
  3.     // 创建action  
  4.     auto action = std::make_unique<Action>(true);  
  5.     std::vector<std::string> name_vector{name};  
  6.   
  7.     // 保证唯一性  
  8.     if (!action->InitSingleTrigger(name)) {  
  9.         return;  
  10.     }  
  11.   
  12.     // 创建action的cmd,指定执行函数和参数  
  13.     action->AddCommand(func, name_vector);  
  14.   
  15.     trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));  
  16.     actions_.emplace_back(std::move(action));  
  17. }  

  QueueBuiltinAction函数中构造新的action加入到actions_中,第一个参数作为新建action携带cmd的执行函数;第二个参数既作为action的trigger name,也作为action携带cmd的参数。

  接下来继续分析main函数:

Java代码
  1. int main(int argc, char** argv) {  
  2.     /* 01. 创建文件系统目录并挂载相关的文件系统 */  
  3.     /* 02. 屏蔽标准的输入输出/初始化内核log系统 */  
  4.     /* 03. 初始化属性域 */  
  5.     /* 04. 完成SELinux相关工作 */•  
  6.     /* 05. 重新设置属性 */  
  7.     /* 06. 创建epoll句柄 */  
  8.     /* 07. 装载子进程信号处理器 */  
  9.     /* 08. 设置默认系统属性 */  
  10.     /* 09. 启动配置属性的服务端 */  
  11.     /* 10. 匹配命令和函数之间的对应关系 */  
  12.     /* 11. 解析init.rc*/  
  13.     /* 12. 向执行队列中添加其他action */  
  14. -------------------------------------------------------------------  
  15.     /* 13. 处理添加到运行队列的事件 */  
  16.     while (true) {  
  17.     // 判断是否有事件需要处理  
  18.         if (!waiting_for_exec) {  
  19.             // 依次执行每个action中携带command对应的执行函数  
  20.      am.ExecuteOneCommand();  
  21.         // 重启一些挂掉的进程  
  22.             restart_processes();  
  23.         }  
  24.   
  25.         // 以下决定timeout的时间,将影响while循环的间隔  
  26.         int timeout = -1;  
  27.         // 有进程需要重启时,等待该进程重启  
  28.         if (process_needs_restart) {  
  29.             timeout = (process_needs_restart - gettime()) * 1000;  
  30.             if (timeout < 0)  
  31.                 timeout = 0;  
  32.         }  
  33.   
  34.         // 有action待处理,不等待  
  35.         if (am.HasMoreCommands()) {  
  36.             timeout = 0;  
  37.         }  
  38.   
  39.         // bootchart_sample应该是进行性能数据采样  
  40.         bootchart_sample(&timeout);  
  41.   
  42.         epoll_event ev;  
  43.         // 没有事件到来的话,最多阻塞timeout时间  
  44.         int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));  
  45.         if (nr == -1) {  
  46.             ERROR("epoll_wait failed: %s\n", strerror(errno));  
  47.         } else if (nr == 1) {  
  48.             //有事件到来,执行对应处理函数  
  49.             //根据上文知道,epoll句柄(即epoll_fd)主要监听子进程结束,及其它进程设置系统属性的请求  
  50.             ((void (*)()) ev.data.ptr)();  
  51.         }  
  52.     }  
  53.     return 0;  
  54. // end main  

  看一下ExecuteOneComand()函数:同样位于system/core/init/action.cpp

Java代码
  1. void ActionManager::ExecuteOneCommand() {  
  2.     // Loop through the trigger queue until we have an action to execute  
  3.     // 当前的可执行action队列为空, trigger_queue_队列不为空  
  4.     while (current_executing_actions_.empty() && !trigger_queue_.empty()) {  
  5.     // 循环遍历action_队列,包含了所有需要执行的命令,解析init.rc获得  
  6.         for (const auto& action : actions_) {  
  7.             // 获取队头的trigger, 检查actions_列表中的action的trigger,对比是否相同  
  8.             if (trigger_queue_.front()->CheckTriggers(*action)) {  
  9.                 // 将所有具有同一trigger的action加入当前可执行action队列  
  10.                 current_executing_actions_.emplace(action.get());  
  11.             }  
  12.         }  
  13.         // 将队头trigger出栈  
  14.         trigger_queue_.pop();  
  15.     }  
  16.   
  17.     if (current_executing_actions_.empty()) {   // 当前可执行的actions队列为空就返回  
  18.         return;  
  19.     }  
  20.   
  21.     auto action = current_executing_actions_.front(); // 获取当前可执行actions队列的首个action  
  22.   
  23.     if (current_command_ == 0) {  
  24.         std::string trigger_name = action->BuildTriggersString();  
  25.         INFO("processing action (%s)\n", trigger_name.c_str());  
  26.     }  
  27.   
  28.     action->ExecuteOneCommand(current_command_);     // 执行当前的命令  
  29.   
  30.     // If this was the last command in the current action, then remove  
  31.     // the action from the executing list.  
  32.     // If this action was oneshot, then also remove it from actions_.  
  33.     ++current_command_;      // 不断叠加,将action_中的所有命令取出  
  34.     if (current_command_ == action->NumCommands()) {  
  35.         current_executing_actions_.pop();  
  36.         current_command_ = 0;  
  37.         if (action->oneshot()) {  
  38.             auto eraser = [&action] (std::unique_ptr<Action>& a) {  
  39.                 return a.get() == action;  
  40.             };  
  41.             actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));  
  42.         }  
  43.     }  
  44. }  

  我们来观察一下init.rc的开头部分:

Java代码
  1. import /init.environ.rc  
  2. import /init.usb.rc  
  3. import /init.${ro.hardware}.rc  
  4. import /init.usb.configfs.rc  
  5. import /init.${ro.zygote}.rc      // 后面我们即将重点分析zygote进程  

  通过ro.zygote的属性import对应的zygote的rc文件。

Android启动篇 — init原理(二)

  我们查看init.zygote64_32.rc:

XML/HTML代码
  1. service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote  
  2.     class main  
  3.     socket zygote stream 660 root system  
  4.     onrestart write /sys/android_power/request_state wake  
  5.     onrestart write /sys/power/state on  
  6.     onrestart restart audioserver  
  7.     onrestart restart cameraserver  
  8.     onrestart restart media  
  9.     onrestart restart netd  
  10.     writepid /dev/cpuset/foreground/tasks  
  11.   
  12. service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary  
  13.     class main  
  14.     socket zygote_secondary stream 660 root system  
  15.     onrestart restart zygote  
  16.     writepid /dev/cpuset/foreground/tasks  

  可以看到zygote的class是main, 它是在on nonencrypted时被启动的,如下:

XML/HTML代码
  1. on boot  
  2.     # basic network init  
  3.     ifup lo  
  4.     hostname localhost  
  5.     domainname localdomain  
  6.     ... ...  
  7.     class_start core  
  8.   
  9. on nonencrypted  
  10.     # A/B update verifier that marks a successful boot.  
  11.     exec - root cache -- /system/bin/update_verifier nonencrypted  
  12.     class_start main  
  13.     class_start late_start  

  至此,Init.cpp的main函数分析完毕!init进程已经启动完成,一些重要的服务如core服务和main服务也都启动起来,并启动了zygote(/system/bin/app_process64)进程,zygote初始化时会创建虚拟机,启动systemserver等。

本文发布:Android开发网
本文地址:http://www.jizhuomi.com/android/course/712.html
2017年9月11日
发布:鸡啄米 分类:Android开发教程 浏览: 评论:0