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,正正在分解代码的历程中银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站会详细先容。

  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文件。银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站要分解的zygote效劳的启动剧本则正正在init.zygoteXX.rc中定义。

  正正在init.rc的import段银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站看到如下代码:

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历程

  接下来,银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站回到源码当中,继续分解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时,银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站为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。

  银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站看一下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. }  

  银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站来观察一下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      // 后面银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站即将浸心分解zygote历程  

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

Android启动篇 — init途理(二)

   银河文娱有哪些网站_云顶文娱场7610_bet9九州 网站查看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开发网
2017年9月11日
楬橥:鸡啄米 分类:Android开发教程 浏览: 评论:0