Android 之init


一、 main函数的一些准备工作

下面我们分析下源码:

  1. int main(int argc, char** argv) {  
  2.     if (!strcmp(basename(argv[0]), "ueventd")) {  
  3.         return ueventd_main(argc, argv);  
  4.     }  
  5.   
  6.     if (!strcmp(basename(argv[0]), "watchdogd")) {  
  7.         return watchdogd_main(argc, argv);  
  8.     }  

由于ueventd watchdogd是公用代码,所以启动的时候根据文件名来判断是哪个进程,继续分析:

  1. // Clear the umask.  
  2. umask(0);  
  3.   
  4. add_environment("PATH", _PATH_DEFPATH);//添加环境变量  
  5.   
  6. bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);  
  7.   
  8. // Get the basic filesystem setup we need put together in the initramdisk  
  9. // on / and then we'll let the rc file figure out the rest.  
  10. if (is_first_stage) {  
  11.     mount("tmpfs""/dev""tmpfs", MS_NOSUID, "mode=0755");  
  12.     mkdir("/dev/pts", 0755);  
  13.     mkdir("/dev/socket", 0755);  
  14.     mount("devpts""/dev/pts""devpts", 0, NULL);  
  15.     mount("proc""/proc""proc", 0, NULL);  
  16.     mount("sysfs""/sys""sysfs", 0, NULL);  
  17. }  

这块代码主要添加环境变量,以及挂载各种文件系统。

  1. open_devnull_stdio();  
  2. klog_init();  
  3. klog_set_level(KLOG_NOTICE_LEVEL);//log的初始化  
  4.   
  5. NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");  
  6.   
  7. if (!is_first_stage) {  
  8.     // Indicate that booting is in progress to background fw loaders, etc.  
  9.     close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//启动的时候创建一个.booting文件  
  10.   
  11.     property_init();//属性初始化  
  12.   
  13.     // If arguments are passed both on the command line and in DT,  
  14.     // properties set in DT always have priority over the command-line ones.  
  15.     process_kernel_dt();  
  16.     process_kernel_cmdline();  
  17.   
  18.     // Propogate the kernel variables to internal variables  
  19.     // used by init as well as the current required properties.  
  20.     export_kernel_boot_props();//设置一些属性  
  21. }  

我们看上面函数先是open_devnull_stdio函数,这个函数就是把标准输入,输出,错误输出重定义到空设备上。然后创建一个 .booting文件代表系统在启动,做了一些属性的初始化,以及一些boot相关的系统属性设置获取等。我们先看下open_devnull_stdio代码:

  1. void open_devnull_stdio(void)  
  2. {  
  3.     // Try to avoid the mknod() call if we can. Since SELinux makes  
  4.     // a /dev/null replacement available for free, let's use it.  
  5.     int fd = open("/sys/fs/selinux/null", O_RDWR);  
  6.     if (fd == -1) {  
  7.         // OOPS, /sys/fs/selinux/null isn't available, likely because  
  8.         // /sys/fs/selinux isn't mounted. Fall back to mknod.  
  9.         static const char *name = "/dev/__null__";  
  10.         if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {  
  11.             fd = open(name, O_RDWR);  
  12.             unlink(name);  
  13.         }  
  14.         if (fd == -1) {  
  15.             exit(1);  
  16.         }  
  17.     }  
  18.   
  19.     dup2(fd, 0);  
  20.     dup2(fd, 1);  
  21.     dup2(fd, 2);  
  22.     if (fd > 2) {  
  23.         close(fd);  
  24.     }  
  25. }  
property_init()函数主要是属性的初始化,这个我们在之前分析属性系统的那篇博客分析过了。
我们再来看process_kernel_dt函数
  1. static void process_kernel_dt(void)  
  2. {  
  3.     static const char android_dir[] = "/proc/device-tree/firmware/android";  
  4.   
  5.     std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);  
  6.   
  7.     std::string dt_file;  
  8.     android::base::ReadFileToString(file_name, &dt_file);  
  9.     if (!dt_file.compare("android,firmware")) {//compatible文件内容是否是android,firmware  
  10.         ERROR("firmware/android is not compatible with 'android,firmware'\n");  
  11.         return;  
  12.     }  
  13.   
  14.     std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);  
  15.     if (!dir)  
  16.         return;  
  17.   
  18.     struct dirent *dp;  
  19.     while ((dp = readdir(dir.get())) != NULL) {//读取目录的每个文件  
  20.         if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible"))  
  21.             continue;  
  22.   
  23.         file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);  
  24.   
  25.         android::base::ReadFileToString(file_name, &dt_file);  
  26.         std::replace(dt_file.begin(), dt_file.end(), ',''.');  
  27.   
  28.         std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);//每个文件名作为属性名,里面的内容作为属性值  
  29.         property_set(property_name.c_str(), dt_file.c_str());  
  30.     }  
  31. }  

上面这个函数主要是在/proc/device-tree/firmware/android 这个目录下,先看compatible文件内容是否是android,firmware。然后这个目录下每个文件名作为属性,文件里面的内容作为属性值。这里话就是ro.boot.hareware ro.boot.name这两个属性值。

  1. root@lte26007:/proc/device-tree/firmware/android # ls  
  2. compatible  
  3. hardware  
  4. name  
继续看process_kernel_cmdline函数
  1. static void process_kernel_cmdline(void)  
  2. {  
  3.     /* don't expose the raw commandline to nonpriv processes */  
  4.     chmod("/proc/cmdline", 0440);  
  5.   
  6.     /* first pass does the common stuff, and finds if we are in qemu. 
  7.      * second pass is only necessary for qemu to export all kernel params 
  8.      * as props. 
  9.      */  
  10.     import_kernel_cmdline(false, import_kernel_nv);  
  11.     if (qemu[0])  
  12.         import_kernel_cmdline(true, import_kernel_nv);  
  13. }  

import_kernel_cmdline函数就是读取proc/cmdline中的内容,然后调用import_kernel_nv函数设置系统属性

  1. void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)  
  2. {  
  3.     char cmdline[2048];  
  4.     char *ptr;  
  5.     int fd;  
  6.   
  7.     fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);  
  8.     if (fd >= 0) {  
  9.         int n = read(fd, cmdline, sizeof(cmdline) - 1);  
  10.         if (n < 0) n = 0;  
  11.   
  12.         /* get rid of trailing newline, it happens */  
  13.         if (n > 0 && cmdline[n-1] == '\n') n--;  
  14.   
  15.         cmdline[n] = 0;  
  16.         close(fd);  
  17.     } else {  
  18.         cmdline[0] = 0;  
  19.     }  
  20.   
  21.     ptr = cmdline;  
  22.     while (ptr && *ptr) {  
  23.         char *x = strchr(ptr, ' ');  
  24.         if (x != 0) *x++ = 0;  
  25.         import_kernel_nv(ptr, in_qemu);  
  26.         ptr = x;  
  27.     }  
  28. }  

在import_kernel_nv函数中设置系统属性,但是一定要有androidboot这样的关键字眼才会设置ro.boot这样的属性。这块在我们的设备cmdline中没有这样的字眼,也就不会设置这些属性。

  1. static void import_kernel_nv(char *name, bool for_emulator)  
  2. {  
  3.     char *value = strchr(name, '=');  
  4.     int name_len = strlen(name);  
  5.   
  6.     if (value == 0) return;  
  7.     *value++ = 0;  
  8.     if (name_len == 0) return;  
  9.   
  10.     if (for_emulator) {  
  11.         /* in the emulator, export any kernel option with the 
  12.          * ro.kernel. prefix */  
  13.         char buff[PROP_NAME_MAX];  
  14.         int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );  
  15.   
  16.         if (len < (int)sizeof(buff))  
  17.             property_set( buff, value );  
  18.         return;  
  19.     }  
  20.   
  21.     if (!strcmp(name,"qemu")) {  
  22.         strlcpy(qemu, value, sizeof(qemu));  
  23.     } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {  
  24.         const char *boot_prop_name = name + 12;  
  25.         char prop[PROP_NAME_MAX];  
  26.         int cnt;  
  27.   
  28.         cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);  
  29.         if (cnt < PROP_NAME_MAX)  
  30.             property_set(prop, value);  
  31.     }  
  32. }  

再来看export_kernel_boot_props这个函数,它也就是设置一些属性,设置ro属性根据之前ro.boot这类的属性值,如果没有设置成unknown,像之前我们有ro.boot.hardware, 那我们就可以设置root.hardware这样的属性。

  1. static void export_kernel_boot_props() {  
  2.     struct {  
  3.         const char *src_prop;  
  4.         const char *dst_prop;  
  5.         const char *default_value;  
  6.     } prop_map[] = {  
  7.         //{ "ro.boot.serialno",   "ro.serialno",   "", },  
  8.         { "ro.boot.mode",       "ro.bootmode",   "unknown", },  
  9.         { "ro.boot.baseband",   "ro.baseband",   "unknown", },  
  10.         { "ro.boot.bootloader""ro.bootloader""unknown", },  
  11.         { "ro.boot.hardware",   "ro.hardware",   "unknown", },  
  12.         { "ro.boot.revision",   "ro.revision",   "0", },  
  13.     };  
  14.     for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {  
  15.         char value[PROP_VALUE_MAX];  
  16.         int rc = property_get(prop_map[i].src_prop, value);  
  17.         property_set(prop_map[i].dst_prop, (rc > 0) ? value : prop_map[i].default_value);  
  18.     }  
  19. }  

下面这块都是selinux相关的,我们就不分析了。

  1. // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.  
  2. selinux_initialize(is_first_stage);  
  3.   
  4. // If we're in the kernel domain, re-exec init to transition to the init domain now  
  5. // that the SELinux policy has been loaded.  
  6. if (is_first_stage) {  
  7.     if (restorecon("/init") == -1) {  
  8.         ERROR("restorecon failed: %s\n", strerror(errno));  
  9.         security_failure();  
  10.     }  
  11.     char* path = argv[0];  
  12.     char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };  
  13.     if (execv(path, args) == -1) {  
  14.         ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));  
  15.         security_failure();  
  16.     }  
  17. }  
  18.   
  19. // These directories were necessarily created before initial policy load  
  20. // and therefore need their security context restored to the proper value.  
  21. // This must happen before /dev is populated by ueventd.  
  22. INFO("Running restorecon...\n");  
  23. restorecon("/dev");  
  24. restorecon("/dev/socket");  
  25. restorecon("/dev/__properties__");  
  26. restorecon_recursive("/sys");  

然后创建了一个epoll的fd

  1. epoll_fd = epoll_create1(EPOLL_CLOEXEC);  
  2. if (epoll_fd == -1) {  
  3.     ERROR("epoll_create1 failed: %s\n", strerror(errno));  
  4.     exit(1);  
  5. }  

继续分析,signal_handler_init函数主要是当子进程被kill之后,会在父进程接受一个信号。处理这个信号的时候往sockpair一端写数据,而另一端的fd是加入的epoll中。这块我们后面会专门其一节讲解。而property_load_boot_defaults就是解析根目录的default.prop中的属性,然后设置到属性中去。start_prperty_service就是把接受属性的socket的fd加入epoll中,也定义了处理函数,属性之前博客专门分析过了。

  1. signal_handler_init();  
  2.   
  3. property_load_boot_defaults();  
  4. start_property_service();  

看看signal_handler_init函数就是处理子进程kill时的情况。

  1. static void SIGCHLD_handler(int) {  
  2.     if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {  
  3.         ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));  
  4.     }  
  5. }  
  6.   
  7. void signal_handler_init() {  
  8.     // Create a signalling mechanism for SIGCHLD.  
  9.     int s[2];  
  10.     if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {  
  11.         ERROR("socketpair failed: %s\n", strerror(errno));  
  12.         exit(1);  
  13.     }  
  14.   
  15.     signal_write_fd = s[0];  
  16.     signal_read_fd = s[1];  
  17.   
  18.     // Write to signal_write_fd if we catch SIGCHLD.  
  19.     struct sigaction act;  
  20.     memset(&act, 0, sizeof(act));  
  21.     act.sa_handler = SIGCHLD_handler;  
  22.     act.sa_flags = SA_NOCLDSTOP;  
  23.     sigaction(SIGCHLD, &act, 0);  
  24.   
  25.     reap_any_outstanding_children();  
  26.   
  27.     register_epoll_handler(signal_read_fd, handle_signal);  
  28. }  


二、解析init.rc

下面我们开始分析解析init.rc并且结合init.rc一起看

init.rc的语言我们可以看这篇博客,主要是init.rc主要有Actions和Service两种,具体看这篇博客http://blog.csdn.net/kc58236582/article/details/52042331

我们通过init_parse_config_file函数来解析init.rc,先把文件数据读取到data中,然后调用parse_config来解析数据。

  1. int init_parse_config_file(const char* path) {  
  2.     INFO("Parsing %s...\n", path);  
  3.     Timer t;  
  4.     std::string data;  
  5.     if (!read_file(path, &data)) {  
  6.         return -1;  
  7.     }  
  8.   
  9.     data.push_back('\n'); // TODO: fix parse_config.  
  10.     parse_config(path, data);  
  11.     dump_parser_state();  
  12.   
  13.     NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());  
  14.     return 0;  
  15. }  

我们先来看看dump_parser_state函数,当解析完之后我们可以在这个函数中打印所有的service和action。

  1. void dump_parser_state() {  
  2.     if (false) {  
  3.         struct listnode* node;  
  4.         list_for_each(node, &service_list) {  
  5.             service* svc = node_to_item(node, struct service, slist);  
  6.             INFO("service %s\n", svc->name);  
  7.             INFO("  class '%s'\n", svc->classname);  
  8.             INFO("  exec");  
  9.             for (int n = 0; n < svc->nargs; n++) {  
  10.                 INFO(" '%s'", svc->args[n]);  
  11.             }  
  12.             INFO("\n");  
  13.             for (socketinfo* si = svc->sockets; si; si = si->next) {  
  14.                 INFO("  socket %s %s 0%o\n", si->name, si->type, si->perm);  
  15.             }  
  16.         }  
  17.   
  18.         list_for_each(node, &action_list) {  
  19.             action* act = node_to_item(node, struct action, alist);  
  20.             INFO("on ");  
  21.             char name_str[256] = "";  
  22.             build_triggers_string(name_str, sizeof(name_str), act);  
  23.             INFO("%s", name_str);  
  24.             INFO("\n");  
  25.   
  26.             struct listnode* node2;  
  27.             list_for_each(node2, &act->commands) {  
  28.                 command* cmd = node_to_item(node2, struct command, clist);  
  29.                 INFO("  %p", cmd->func);  
  30.                 for (int n = 0; n < cmd->nargs; n++) {  
  31.                     INFO(" %s", cmd->args[n]);  
  32.                 }  
  33.                 INFO("\n");  
  34.             }  
  35.             INFO("\n");  
  36.         }  
  37.     }  
  38. }  

好回到正题看parse_config函数,来解析从init.rc文件中获取的数据。

  1. static void parse_config(const char *fn, const std::string& data)  
  2. {  
  3.     struct listnode import_list;  
  4.     struct listnode *node;  
  5.     char *args[INIT_PARSER_MAXARGS];  
  6.   
  7.     int nargs = 0;  
  8.   
  9.     parse_state state;  
  10.     state.filename = fn;  
  11.     state.line = 0;  
  12.     state.ptr = strdup(data.c_str());  // TODO: fix this code!  
  13.     state.nexttoken = 0;  
  14.     state.parse_line = parse_line_no_op;//这里的函数是空实现  
  15.   
  16.     list_init(&import_list);  
  17.     state.priv = &import_list;  
  18.   
  19.     for (;;) {  
  20.         switch (next_token(&state)) {  
  21.         case T_EOF:  
  22.             state.parse_line(&state, 0, 0);  
  23.             goto parser_done;  
  24.         case T_NEWLINE:  
  25.             state.line++;  
  26.             if (nargs) {  
  27.                 int kw = lookup_keyword(args[0]);  
  28.                 if (kw_is(kw, SECTION)) {  
  29.                     state.parse_line(&state, 0, 0);  
  30.                     parse_new_section(&state, kw, nargs, args);  
  31.                 } else {  
  32.                     state.parse_line(&state, nargs, args);  
  33.                 }  
  34.                 nargs = 0;  
  35.             }  
  36.             break;  
  37.         case T_TEXT:  
  38.             if (nargs < INIT_PARSER_MAXARGS) {  
  39.                 args[nargs++] = state.text;  
  40.             }  
  41.             break;  
  42.         }  
  43.     }  
  44.   
  45. parser_done:  
  46.     list_for_each(node, &import_list) {  
  47.          struct import *import = node_to_item(node, struct import, list);  
  48.          int ret;  
  49.   
  50.          ret = init_parse_config_file(import->filename);  
  51.          if (ret)  
  52.              ERROR("could not import file '%s' from '%s'\n",  
  53.                    import->filename, fn);  
  54.     }  
  55. }  

我们先来看看next_token函数,我们来看下这个函数,

  1. int next_token(struct parse_state *state)  
  2. {  
  3.     char *x = state->ptr;  
  4.     char *s;  
  5.   
  6.     if (state->nexttoken) {//刚进来为空  
  7.         int t = state->nexttoken;  
  8.         state->nexttoken = 0;  
  9.         return t;  
  10.     }  
  11.   
  12.     for (;;) {  
  13.         switch (*x) {  
  14.         case 0:  
  15.             state->ptr = x;  
  16.             return T_EOF;  
  17.         case '\n':  
  18.             x++;  
  19.             state->ptr = x;  
  20.             return T_NEWLINE;  
  21.         case ' ':  
  22.         case '\t':  
  23.         case '\r':  
  24.             x++;  
  25.             continue;  
  26.         case '#':  
  27.             while (*x && (*x != '\n')) x++;  
  28.             if (*x == '\n') {  
  29.                 state->ptr = x+1;  
  30.                 return T_NEWLINE;  
  31.             } else {  
  32.                 state->ptr = x;  
  33.                 return T_EOF;  
  34.             }  
  35.         default://刚进来肯定直接是这个  
  36.             goto text;  
  37.         }  
  38.     }  
  39.   
  40. textdone:  
  41.     state->ptr = x;  
  42.     *s = 0;  
  43.     return T_TEXT;  
  44. text:  
  45.     state->text = s = x;//赋值state->text  
  46. textresume:  
  47.     for (;;) {  
  48.         switch (*x) {  
  49.         case 0:  
  50.             goto textdone;  
  51.         case ' ':  
  52.         case '\t':  
  53.         case '\r'://碰到空什么的,直接返回T_TEXT  
  54.             x++;  
  55.             goto textdone;  
  56.         case '\n':  
  57.             state->nexttoken = T_NEWLINE;//碰到回车换行直接nexttoken是newline  
  58.             x++;  
  59.             goto textdone;  
  60.         case '"':  
  61.             x++;  
  62.             for (;;) {  
  63.                 switch (*x) {  
  64.                 case 0:  
  65.                         /* unterminated quoted thing */  
  66.                     state->ptr = x;  
  67.                     return T_EOF;  
  68.                 case '"':  
  69.                     x++;  
  70.                     goto textresume;  
  71.                 default:  
  72.                     *s++ = *x++;  
  73.                 }  
  74.             }  
  75.             break;  
  76.         case '\\':  
  77.             x++;  
  78.             switch (*x) {  
  79.             case 0:  
  80.                 goto textdone;  
  81.             case 'n':  
  82.                 *s++ = '\n';  
  83.                 break;  
  84.             case 'r':  
  85.                 *s++ = '\r';  
  86.                 break;  
  87.             case 't':  
  88.                 *s++ = '\t';  
  89.                 break;  
  90.             case '\\':  
  91.                 *s++ = '\\';  
  92.                 break;  
  93.             case '\r':  
  94.                     /* \ <cr> <lf> -> line continuation */  
  95.                 if (x[1] != '\n') {  
  96.                     x++;  
  97.                     continue;  
  98.                 }  
  99.             case '\n':  
  100.                     /* \ <lf> -> line continuation */  
  101.                 state->line++;  
  102.                 x++;  
  103.                     /* eat any extra whitespace */  
  104.                 while((*x == ' ') || (*x == '\t')) x++;  
  105.                 continue;  
  106.             default:  
  107.                     /* unknown escape -- just copy */  
  108.                 *s++ = *x++;  
  109.             }  
  110.             continue;  
  111.         default:  
  112.             *s++ = *x++;//一般的值继续往前走  
  113.         }  
  114.     }  
  115.     return T_EOF;  
  116. }  

看这个函数的代码,我们只需要知道。当我们普通的进来,没有碰到换行,只有碰到空格的话,返回T_TEXT,并且nextoken为null。

我们再来看T_TEXT的时候只是在数组里面保存了state.text的内容,然后继续下一次。当我们直到碰到/n,回车换行。这个时候返回T_TEXT,但是nexttoken是T_NEWLINE

这样下次,就直接返回T_NEWLINE了,当返回T_NEWLINE直接调用lookup_keyword函数。

  1. for (;;) {  
  2.     switch (next_token(&state)) {  
  3.     case T_EOF:  
  4.         state.parse_line(&state, 0, 0);  
  5.         goto parser_done;  
  6.     case T_NEWLINE:  
  7.         state.line++;  
  8.         if (nargs) {  
  9.             int kw = lookup_keyword(args[0]);  
  10.             if (kw_is(kw, SECTION)) {  
  11.                 state.parse_line(&state, 0, 0);  
  12.                 parse_new_section(&state, kw, nargs, args);  
  13.             } else {  
  14.                 state.parse_line(&state, nargs, args);  
  15.             }  
  16.             nargs = 0;  
  17.         }  
  18.         break;  
  19.     case T_TEXT:  
  20.         if (nargs < INIT_PARSER_MAXARGS) {  
  21.             args[nargs++] = state.text;  
  22.         }  
  23.         break;  
  24.     }  
  25. }  

lookup_keyword函数就是看第一个单词返回一个K_**的值而已。

  1. static int lookup_keyword(const char *s)  
  2. {  
  3.     switch (*s++) {  
  4.     case 'b':  
  5.         if (!strcmp(s, "ootchart_init")) return K_bootchart_init;  
  6.         break;  
  7.     case 'c':  
  8.         if (!strcmp(s, "opy")) return K_copy;  
  9.         if (!strcmp(s, "lass")) return K_class;  
  10.         if (!strcmp(s, "lass_start")) return K_class_start;  
  11.         if (!strcmp(s, "lass_stop")) return K_class_stop;  
  12.         if (!strcmp(s, "lass_reset")) return K_class_reset;  
  13.         if (!strcmp(s, "onsole")) return K_console;  
  14.         if (!strcmp(s, "hown")) return K_chown;  
  15.         if (!strcmp(s, "hmod")) return K_chmod;  
  16.         if (!strcmp(s, "ritical")) return K_critical;  
  17.         break;  
  18.     case 'd':  
  19.         if (!strcmp(s, "isabled")) return K_disabled;  
  20.         if (!strcmp(s, "omainname")) return K_domainname;  
  21.         break;  
  22.     case 'e':  
  23.         if (!strcmp(s, "nable")) return K_enable;  
  24.         if (!strcmp(s, "xec")) return K_exec;  
  25.         if (!strcmp(s, "xport")) return K_export;  
  26.         break;  
  27.     case 'g':  
  28.         if (!strcmp(s, "roup")) return K_group;  
  29.         break;  
  30.     case 'h':  
  31.         if (!strcmp(s, "ostname")) return K_hostname;  
  32.         break;  
  33.     case 'i':  
  34.         if (!strcmp(s, "oprio")) return K_ioprio;  
  35.         if (!strcmp(s, "fup")) return K_ifup;  
  36.         if (!strcmp(s, "nsmod")) return K_insmod;  
  37.         if (!strcmp(s, "mport")) return K_import;  
  38.         if (!strcmp(s, "nstallkey")) return K_installkey;  
  39.         break;  
  40.     case 'k':  
  41.         if (!strcmp(s, "eycodes")) return K_keycodes;  
  42.         break;  
  43.     case 'l':  
  44.         if (!strcmp(s, "oglevel")) return K_loglevel;  
  45.         if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;  
  46.         if (!strcmp(s, "oad_system_props")) return K_load_system_props;  
  47.         break;  
  48.     case 'm':  
  49.         if (!strcmp(s, "kdir")) return K_mkdir;  
  50.         if (!strcmp(s, "ount_all")) return K_mount_all;  
  51.         if (!strcmp(s, "ount")) return K_mount;  
  52.         break;  

再来看这个宏

  1. #define kw_is(kw, type) (keyword_info[kw].flags & (type))  

来看看它的定义,首先先说下宏定义##代表后面是连接起来的,#代表就是后面这个变量

  1. #define KEYWORD(symbol, flags, nargs, func) \  
  2.     [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },  
  3.   
  4. static struct {  
  5.     const char *name;  
  6.     int (*func)(int nargs, char **args);  
  7.     unsigned char nargs;  
  8.     unsigned char flags;  
  9. } keyword_info[KEYWORD_COUNT] = {  
  10.     [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },  
  11. #include "keywords.h"  
  12. };  

这样我们再来看下keywords.h这个头文件,这里就比较明白是它是解析各个关键词是属于SECTION,COMMAND,OPTION的

  1. #ifndef KEYWORD//因为前面定义了KEYWORD  
  2. int do_bootchart_init(int nargs, char **args);  
  3. ......  
  4. #endif  
  5.     KEYWORD(bootchart_init,        COMMAND, 0, do_bootchart_init)  
  6.     KEYWORD(chmod,       COMMAND, 2, do_chmod)  
  7.     KEYWORD(chown,       COMMAND, 2, do_chown)  
  8.     KEYWORD(class,       OPTION,  0, 0)  
  9. ......  
  10.     KEYWORD(import,      SECTION, 1, 0)  
  11. .....  
  12. .....  
  13.     KEYWORD(service,     SECTION, 0, 0)  
  14.     KEYWORD(writepid,    OPTION,  0, 0)  
  15. #ifdef __MAKE_KEYWORD_ENUM__  
  16.     KEYWORD_COUNT,  
  17. };  
  18. #undef __MAKE_KEYWORD_ENUM__  
  19. #undef KEYWORD  
  20. #endif  

这样我们就可以通过kw_is(kw, SECTION)来判断是否属于SECTION

我们来看下函数,如果是SECTION,刚开始调用state.parse_line也是空实现

  1. if (kw_is(kw, SECTION)) {  
  2.     state.parse_line(&state, 0, 0);  
  3.     parse_new_section(&state, kw, nargs, args);  
  4. else {  
  5.     state.parse_line(&state, nargs, args);  
  6. }  

再来看看parse_new_section函数

  1. static void parse_new_section(struct parse_state *state, int kw,  
  2.                        int nargs, char **args)  
  3. {  
  4.     printf("[ %s %s ]\n", args[0],  
  5.            nargs > 1 ? args[1] : "");  
  6.     switch(kw) {  
  7.     case K_service://如果是service  
  8.         state->context = parse_service(state, nargs, args);  
  9.         if (state->context) {  
  10.             state->parse_line = parse_line_service;  
  11.             return;  
  12.         }  
  13.         break;  
  14.     case K_on://是on  
  15.         state->context = parse_action(state, nargs, args);  
  16.         if (state->context) {  
  17.             state->parse_line = parse_line_action;  
  18.             return;  
  19.         }  
  20.         break;  
  21.     case K_import://是import  
  22.         parse_import(state, nargs, args);  
  23.         break;  
  24.     }  
  25.     state->parse_line = parse_line_no_op;  
  26. }  


2.1 解析service

我们先来看下如果是service,先调用parse_service函数

  1. static void *parse_service(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     if (nargs < 3) {  
  4.         parse_error(state, "services must have a name and a program\n");  
  5.         return 0;  
  6.     }  
  7.     if (!valid_name(args[1])) {  
  8.         parse_error(state, "invalid service name '%s'\n", args[1]);  
  9.         return 0;  
  10.     }  
  11.   
  12.     service* svc = (service*) service_find_by_name(args[1]);//找service  
  13.     if (svc) {//如果找到该service,说明重复了  
  14.         parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);  
  15.         return 0;  
  16.     }  
  17.   
  18.     nargs -= 2;  
  19.     svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);//new一个service  
  20.     if (!svc) {  
  21.         parse_error(state, "out of memory\n");  
  22.         return 0;  
  23.     }  
  24.     svc->name = strdup(args[1]);//各种初始化  
  25.     svc->classname = "default";  
  26.     memcpy(svc->args, args + 2, sizeof(char*) * nargs);  
  27.     trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));  
  28.     svc->args[nargs] = 0;  
  29.     svc->nargs = nargs;  
  30.     list_init(&svc->onrestart.triggers);  
  31.     cur_trigger->name = "onrestart";  
  32.     list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);  
  33.     list_init(&svc->onrestart.commands);  
  34.     list_add_tail(&service_list, &svc->slist);//把service放进service_list  
  35.     return svc;  
  36. }  

state->parse_line赋值了parse_line_service函数了。然后我们再出这个函数看看,当你再来一行新的,这个时候不是SECTION,就要调用parse_line_service函数来解析了。

  1. case T_NEWLINE:  
  2.     state.line++;  
  3.     if (nargs) {  
  4.         int kw = lookup_keyword(args[0]);  
  5.         if (kw_is(kw, SECTION)) {  
  6.             state.parse_line(&state, 0, 0);  
  7.             parse_new_section(&state, kw, nargs, args);  
  8.         } else {  
  9.             state.parse_line(&state, nargs, args);  
  10.         }  
  11.         nargs = 0;  
  12.     }  
  13.     break;  

我们来看下parse_line_service函数:下面就是解析各种参数,然后填充service变量而已。

  1. static void parse_line_service(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct service *svc = (service*) state->context;  
  4.     struct command *cmd;  
  5.     int i, kw, kw_nargs;  
  6.   
  7.     if (nargs == 0) {  
  8.         return;  
  9.     }  
  10.   
  11.     svc->ioprio_class = IoSchedClass_NONE;  
  12.   
  13.     kw = lookup_keyword(args[0]);  
  14.     switch (kw) {  
  15.     case K_class:  
  16.         if (nargs != 2) {  
  17.             parse_error(state, "class option requires a classname\n");  
  18.         } else {  
  19.             svc->classname = args[1];  
  20.         }  
  21.         break;  
  22.     case K_console:  
  23.         svc->flags |= SVC_CONSOLE;  
  24.         break;  
  25.     case K_disabled:  


2.2 解析on关键字

下面我们来看下解析on关键字的

  1. case K_on:  
  2.     state->context = parse_action(state, nargs, args);  
  3.     if (state->context) {  
  4.         state->parse_line = parse_line_action;  
  5.         return;  
  6.     }  
  7.     break;  
先看下parse_action函数
  1. static void *parse_action(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct trigger *cur_trigger;  
  4.     int i;  
  5.     if (nargs < 2) {  
  6.         parse_error(state, "actions must have a trigger\n");  
  7.         return 0;  
  8.     }  
  9.   
  10.     action* act = (action*) calloc(1, sizeof(*act));//新建aciton  
  11.     list_init(&act->triggers);  
  12.   
  13.     for (i = 1; i < nargs; i++) {  
  14.         if (!(i % 2)) {  
  15.             if (strcmp(args[i], "&&")) {//有的触发器有几个条件,比如可以两个属性同事满足  
  16.                 struct listnode *node;  
  17.                 struct listnode *node2;  
  18.                 parse_error(state, "& is the only symbol allowed to concatenate actions\n");  
  19.                 list_for_each_safe(node, node2, &act->triggers) {  
  20.                     struct trigger *trigger = node_to_item(node, struct trigger, nlist);  
  21.                     free(trigger);  
  22.                 }  
  23.                 free(act);  
  24.                 return 0;  
  25.             } else  
  26.                 continue;  
  27.         }  
  28.         cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));  
  29.         cur_trigger->name = args[i];  
  30.         list_add_tail(&act->triggers, &cur_trigger->nlist);  
  31.     }  
  32.   
  33.     list_init(&act->commands);  
  34.     list_init(&act->qlist);  
  35.     list_add_tail(&action_list, &act->alist);//把aciton加入action_list中  
  36.         /* XXX add to hash */  
  37.     return act;  
  38. }  

这里新建一个action,然后加入action_list中。主要触发器可以有几个条件。比如满足两个属性要求,然后保存在action的的triggers中。

同样我们再来看看parse_line_action函数,这个函数就是各种命令了。

  1. static void parse_line_action(struct parse_state* state, int nargs, char **args)  
  2. {  
  3.     struct action *act = (action*) state->context;  
  4.     int kw, n;  
  5.   
  6.     if (nargs == 0) {  
  7.         return;  
  8.     }  
  9.   
  10.     kw = lookup_keyword(args[0]);  
  11.     if (!kw_is(kw, COMMAND)) {  
  12.         parse_error(state, "invalid command '%s'\n", args[0]);  
  13.         return;  
  14.     }  
  15.   
  16.     n = kw_nargs(kw);  
  17.     if (nargs < n) {  
  18.         parse_error(state, "%s requires %d %s\n", args[0], n - 1,  
  19.             n > 2 ? "arguments" : "argument");  
  20.         return;  
  21.     }  
  22.     command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);  
  23.     cmd->func = kw_func(kw);  
  24.     cmd->line = state->line;  
  25.     cmd->filename = state->filename;  
  26.     cmd->nargs = nargs;  
  27.     memcpy(cmd->args, args, sizeof(char*) * nargs);  
  28.     list_add_tail(&act->commands, &cmd->clist);// 加入到act->commands  
  29. }  

这里注意是kw_func宏,就是和之前那个宏一样,这里是选择每个命令的处理函数。


2.3 处理import

处理import我们来看下parse_import函数,这个函数很简单就把import的文件名保存在import_list中。

  1. static void parse_import(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct listnode *import_list = (listnode*) state->priv;  
  4.     char conf_file[PATH_MAX];  
  5.     int ret;  
  6.   
  7.     if (nargs != 2) {  
  8.         ERROR("single argument needed for import\n");  
  9.         return;  
  10.     }  
  11.   
  12.     ret = expand_props(conf_file, args[1], sizeof(conf_file));  
  13.     if (ret) {  
  14.         ERROR("error while handling import on line '%d' in '%s'\n",  
  15.               state->line, state->filename);  
  16.         return;  
  17.     }  
  18.   
  19.     struct import* import = (struct import*) calloc(1, sizeof(struct import));  
  20.     import->filename = strdup(conf_file);  
  21.     list_add_tail(import_list, &import->list);  
  22.     INFO("Added '%s' to import list\n", import->filename);  
  23. }  

最后我们来看下当所有init.rc中的关键字解析完之后,就会遍历import_list,然后调用init_parse_config_file函数再来解析该文件。

  1. parser_done:  
  2.     list_for_each(node, &import_list) {  
  3.          struct import *import = node_to_item(node, struct import, list);  
  4.          int ret;  
  5.   
  6.          ret = init_parse_config_file(import->filename);  
  7.          if (ret)  
  8.              ERROR("could not import file '%s' from '%s'\n",  
  9.                    import->filename, fn);  
  10.     }  

所以一般在init.rc中import的文件,放入action service列表中,会比直接在init.rc中的service和aciton靠后。


三、加入执行队列

在解析init.rc文件后,这节将介绍把Action加入执行队列中。

  1. action_for_each_trigger("early-init", action_add_queue_tail);  
  2.   
  3. // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...  
  4. queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  5. // ... so that we can start queuing up actions that require stuff from /dev.  
  6. queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  7. queue_builtin_action(keychord_init_action, "keychord_init");  
  8. queue_builtin_action(console_init_action, "console_init");  
  9.   
  10. // Trigger all the boot actions to get us started.  
  11. action_for_each_trigger("init", action_add_queue_tail);  
  12.   
  13. // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random  
  14. // wasn't ready immediately after wait_for_coldboot_done  
  15. queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  16.   
  17. // Don't mount filesystems or start core system services in charger mode.  
  18. char bootmode[PROP_VALUE_MAX];  
  19. if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {  
  20.     action_for_each_trigger("charger", action_add_queue_tail);  
  21. else {  
  22.     action_for_each_trigger("late-init", action_add_queue_tail);  
  23. }  
  24.   
  25. // Run all property triggers based on current state of the properties.  
  26. queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");  

我们先来看action_for_each_trigger函数

  1. void action_for_each_trigger(const char *trigger,  
  2.                              void (*func)(struct action *act))  
  3. {  
  4.     struct listnode *node, *node2;  
  5.     struct action *act;  
  6.     struct trigger *cur_trigger;  
  7.   
  8.     list_for_each(node, &action_list) {//遍历每个action  
  9.         act = node_to_item(node, struct action, alist);  
  10.         list_for_each(node2, &act->triggers) {//遍历每个action的triggers  
  11.             cur_trigger = node_to_item(node2, struct trigger, nlist);  
  12.             if (!strcmp(cur_trigger->name, trigger)) {//是否与传入的trigger名字匹配  
  13.                 func(act);//调用回调函数  
  14.             }  
  15.         }  
  16.     }  
  17. }  

我们再来看下传入的回调函数action_add_queue_tail,这个函数就是把aciton加入执行列表中。

  1. void action_add_queue_tail(struct action *act)  
  2. {  
  3.     if (list_empty(&act->qlist)) {  
  4.         list_add_tail(&action_queue, &act->qlist);  
  5.     }  
  6. }  

1. 这样的话像第一句,就是在所有的aciton中是否有early-init这样的trigger,有的话加入执行列表。

  1. action_for_each_trigger("early-init", action_add_queue_tail);  

我们看下init.rc中early-init中的内容,设置了init进程的adj,开启ueventd进程等。

  1. on early-init  
  2.     # Set init and its forked children's oom_adj.  
  3.     write /proc/1/oom_score_adj -1000  
  4.   
  5.     # Set the security context of /adb_keys if present.  
  6.     restorecon /adb_keys  
  7.   
  8.     start ueventd  
  9.   
  10.     #add for amt  
  11.     mkdir /amt 0775 root system  

下面我们再来看下queue_builtin_action函数,这个函数的话就是直接创建一个action,然后新建command,关键是func会调函数设置好。最后把action加入执行队列中。

  1. void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)  
  2. {  
  3.     action* act = (action*) calloc(1, sizeof(*act));  
  4.     trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));  
  5.     cur_trigger->name = name;  
  6.     list_init(&act->triggers);  
  7.     list_add_tail(&act->triggers, &cur_trigger->nlist);  
  8.     list_init(&act->commands);  
  9.     list_init(&act->qlist);  
  10.   
  11.     command* cmd = (command*) calloc(1, sizeof(*cmd));  
  12.     cmd->func = func;  
  13.     cmd->args[0] = const_cast<char*>(name);  
  14.     cmd->nargs = 1;  
  15.     list_add_tail(&act->commands, &cmd->clist);  
  16.   
  17.     list_add_tail(&action_list, &act->alist);  
  18.     action_add_queue_tail(act);  
  19. }  

2. 因此这里我们看下wait_for_coldboot_done_action函数,这函数就是等待/dev/.coldboot_done文件

  1. static int wait_for_coldboot_done_action(int nargs, char **args) {  
  2.     Timer t;  
  3.   
  4.     NOTICE("Waiting for %s...\n", COLDBOOT_DONE);  
  5.     // Any longer than 1s is an unreasonable length of time to delay booting.  
  6.     // If you're hitting this timeout, check that you didn't make your  
  7.     // sepolicy regular expressions too expensive (http://b/19899875).  
  8.     if (wait_for_file(COLDBOOT_DONE, 1)) {  
  9.         ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);  
  10.     }  
  11.   
  12.     NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());  
  13.     

wait_for_file等待/dev/.coldboot_done文件,超时时间设置的是1秒。

  1. int wait_for_file(const char *filename, int timeout)  
  2. {  
  3.     struct stat info;  
  4.     uint64_t timeout_time_ns = gettime_ns() + timeout * UINT64_C(1000000000);  
  5.     int ret = -1;  
  6.   
  7.     while (gettime_ns() < timeout_time_ns && ((ret = stat(filename, &info)) < 0))  
  8.         usleep(10000);  
  9.   
  10.     return ret;  
  11. }  

3. mix_hwrng_into_linux_rng_action函数从硬件PNG的设备文件/dev/hw_random读取512字节并写到LinuxRNG设备文件dev/urandom中。

4. keychord_init_action初始化组合键监听模块,这个函数调用了keychord_init函数

  1. static int keychord_init_action(int nargs, char **args)  
  2. {  
  3.     keychord_init();  
  4.     return 0;  
  5. }  
  1. void keychord_init() {  
  2.     service_for_each(add_service_keycodes);  
  3.   
  4.     // Nothing to do if no services require keychords.  
  5.     if (!keychords) {  
  6.         return;  
  7.     }  
  8.   
  9.     keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));  
  10.     if (keychord_fd == -1) {  
  11.         ERROR("could not open /dev/keychord: %s\n", strerror(errno));  
  12.         return;  
  13.     }  
  14.   
  15.     int ret = write(keychord_fd, keychords, keychords_length);  
  16.     if (ret != keychords_length) {  
  17.         ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));  
  18.         close(keychord_fd);  
  19.     }  
  20.   
  21.     free(keychords);  
  22.     keychords = nullptr;  
  23.   
  24.     register_epoll_handler(keychord_fd, handle_keychord);  
  25. }  
keychord_init函数先是遍历各个service,然后调用add_service_keycodes函数,在add_service_keycodes函数中,主要看service有没有keycodes这个变量,有的话将新建一个keychord,然后将service的keycodes保存在这个变量中。最后还有一个全局的keychords,所以的数据最后都是可以通过这个全局指针找到。
  1. void add_service_keycodes(struct service *svc)  
  2. {  
  3.     struct input_keychord *keychord;  
  4.     int i, size;  
  5.   
  6.     if (svc->keycodes) {  
  7.         /* add a new keychord to the list */  
  8.         size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);  
  9.         keychords = (input_keychord*) realloc(keychords, keychords_length + size);  
  10.         if (!keychords) {  
  11.             ERROR("could not allocate keychords\n");  
  12.             keychords_length = 0;  
  13.             keychords_count = 0;  
  14.             return;  
  15.         }  
  16.   
  17.         keychord = (struct input_keychord *)((char *)keychords + keychords_length);  
  18.         keychord->version = KEYCHORD_VERSION;  
  19.         keychord->id = keychords_count + 1;  
  20.         keychord->count = svc->nkeycodes;  
  21.         svc->keychord_id = keychord->id;  
  22.   
  23.         for (i = 0; i < svc->nkeycodes; i++) {  
  24.             keychord->keycodes[i] = svc->keycodes[i];  
  25.         }  
  26.         keychords_count++;  
  27.         keychords_length += size;  
  28.     }  
  29. }  

然后我们把keychords这个全局变量数据写入/dev/keychord文件中,最后调用register_epoll_handler函数把这个fd注册到epoll中。

  1. int ret = write(keychord_fd, keychords, keychords_length);  
  2. if (ret != keychords_length) {  
  3.     ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));  
  4.     close(keychord_fd);  
  5. }  
  6.   
  7. free(keychords);  
  8. keychords = nullptr;  
  9.   
  10. register_epoll_handler(keychord_fd, handle_keychord);  

最后在这个fd有数据来的时候,我们读取出来,通过service_find_by_keychord看与哪个service的的keychord匹配,匹配的话就把service启动。但是前提是and_enabled是running。

  1. static void handle_keychord() {  
  2.     struct service *svc;  
  3.     char adb_enabled[PROP_VALUE_MAX];  
  4.     int ret;  
  5.     __u16 id;  
  6.   
  7.     // Only handle keychords if adb is enabled.  
  8.     property_get("init.svc.adbd", adb_enabled);  
  9.     ret = read(keychord_fd, &id, sizeof(id));  
  10.     if (ret != sizeof(id)) {  
  11.         ERROR("could not read keychord id\n");  
  12.         return;  
  13.     }  
  14.   
  15.     if (!strcmp(adb_enabled, "running")) {  
  16.         svc = service_find_by_keychord(id);  
  17.         if (svc) {  
  18.             INFO("Starting service %s from keychord\n", svc->name);  
  19.             service_start(svc, NULL);  
  20.         } else {  
  21.             ERROR("service for keychord %d not found\n", id);  
  22.         }  
  23.     }  
  24. }  

5. console_init_action是显示A N D R O I D 字样的logo。

  1. static int console_init_action(int nargs, char **args)  
  2. {  
  3.     char console[PROP_VALUE_MAX];  
  4.     if (property_get("ro.boot.console", console) > 0) {  
  5.         snprintf(console_name, sizeof(console_name), "/dev/%s", console);  
  6.     }  
  7.   
  8.     int fd = open(console_name, O_RDWR | O_CLOEXEC);  
  9.     if (fd >= 0)  
  10.         have_console = 1;//是否有控制台  
  11.     close(fd);  
  12.   
  13.     fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);  
  14.     if (fd >= 0) {  
  15.         const char *msg;  
  16.             msg = "\n"  
  17.         "\n"  
  18.         "\n"  
  19.         "\n"  
  20.         "\n"  
  21.         "\n"  
  22.         "\n"  // console is 40 cols x 30 lines  
  23.         "\n"  
  24.         "\n"  
  25.         "\n"  
  26.         "\n"  
  27.         "\n"  
  28.         "\n"  
  29.         "\n"  
  30.         "             A N D R O I D ";  
  31.         write(fd, msg, strlen(msg));//显示android字样  
  32.         close(fd);  
  33.     }  
  34.   
  35.     return 0;  
  36. }  

6. action_for_each_trigger("init", action_add_queue_tail); 触发init触发器, 主要是mount一些设备,还有创建一些目录。

  1. on init  
  2.     sysclktz 0  
  3.   
  4.     # Backward compatibility.  
  5.     symlink /system/etc /etc  
  6.     symlink /sys/kernel/debug /d  
  7.   
  8.     # Link /vendor to /system/vendor for devices without a vendor partition.  
  9.     symlink /system/vendor /vendor  
  10.   
  11.     # Create cgroup mount point for cpu accounting  
  12.     mkdir /acct  
  13.     mount cgroup none /acct cpuacct  
  14.     mkdir /acct/uid  
  15.   
  16.     # Create cgroup mount point for memory  
  17.     mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000  
  18. ......  

7. mix_hwrng_into_linux_rng_action也是和RNG相关
8. charger和late-init,根据ro.bootmode来触发charger还是late-init触发器

  1. char bootmode[PROP_VALUE_MAX];  
  2. if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {  
  3.     action_for_each_trigger("charger", action_add_queue_tail);  
  4. else {  
  5.     action_for_each_trigger("late-init", action_add_queue_tail);  
  6. }  

late-init内容如下:

  1. on late-init  
  2.     trigger early-fs  
  3.     trigger fs  
  4.     trigger post-fs  
  5.   
  6.     # Load properties from /system/ + /factory after fs mount. Place  
  7.     # this in another action so that the load will be scheduled after the prior  
  8.     # issued fs triggers have completed.  
  9.     trigger load_system_props_action//加载系统属性  
  10.   
  11.     # Now we can mount /data. File encryption requires keymaster to decrypt  
  12.     # /data, which in turn can only be loaded when system properties are present  
  13.     trigger post-fs-data  
  14.     trigger load_persist_props_action//加载persist属性  
  15.   
  16.     # Remove a file to wake up anything waiting for firmware.  
  17.     trigger firmware_mounts_complete  
  18.   
  19.     trigger early-boot  
  20.     trigger boot//这里面启动main core服务  

而on charger就会启动一个charger进程

  1. on charger  
  2.     class_start charger  
  1. service charger /charger  
  2.     seclabel u:r:healthd:s0  
  3.     oneshot  

9. queue_property_triggers_action就是看现在那些aciton满足条件,把它加入执行列中。

  1. static int queue_property_triggers_action(int nargs, char **args)  
  2. {  
  3.     queue_all_property_triggers();  
  4.     /* enable property triggers */  
  5.     property_triggers_enabled = 1;  
  6.     return 0;  
  7. }  


  1. void queue_all_property_triggers()  
  2. {  
  3.     queue_property_triggers(NULL, NULL);  
  4. }  

最后调用queue_property_triggers,遍历所有的aciton是属性的那种,只要满足条件加入执行队列。

  1. void queue_property_triggers(const char *name, const char *value)  
  2. {  
  3.     struct listnode *node, *node2;  
  4.     struct action *act;  
  5.     struct trigger *cur_trigger;  
  6.     bool match;  
  7.     int name_length;  
  8.   
  9.     list_for_each(node, &action_list) {  
  10.         act = node_to_item(node, struct action, alist);  
  11.         match = !name;  
  12.         list_for_each(node2, &act->triggers) {  
  13.             cur_trigger = node_to_item(node2, struct trigger, nlist);  
  14.             if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {  
  15.                 const char *test = cur_trigger->name + strlen("property:");  
  16.                 if (!match) {  
  17.                     name_length = strlen(name);  
  18.                     if (!strncmp(name, test, name_length) &&  
  19.                         test[name_length] == '=' &&  
  20.                         (!strcmp(test + name_length + 1, value) ||  
  21.                         !strcmp(test + name_length + 1, "*"))) {  
  22.                         match = true;  
  23.                         continue;  
  24.                     }  
  25.                 }  
  26.                 const char* equals = strchr(test, '=');  
  27.                 if (equals) {  
  28.                     char prop_name[PROP_NAME_MAX + 1];  
  29.                     char value[PROP_VALUE_MAX];  
  30.                     int length = equals - test;  
  31.                     if (length <= PROP_NAME_MAX) {  
  32.                         int ret;  
  33.                         memcpy(prop_name, test, length);  
  34.                         prop_name[length] = 0;  
  35.   
  36.                         /* does the property exist, and match the trigger value? */  
  37.                         ret = property_get(prop_name, value);  
  38.                         if (ret > 0 && (!strcmp(equals + 1, value) ||  
  39.                                         !strcmp(equals + 1, "*"))) {  
  40.                             continue;  
  41.                         }  
  42.                     }  
  43.                 }  
  44.             }  
  45.             match = false;  
  46.             break;  
  47.         }  
  48.         if (match) {  
  49.             action_add_queue_tail(act);  
  50.         }  
  51.     }  
  52. }  


四、属性系统

属性会在start_property_service函数中,把属性的socket 的fd加入到了epoll中,init主要是检测属性发生改变时,有哪些action满足条件需要触发。以及一些persist属性保存。ctl属性开启 关闭service等。

具体的我们在之前的博客http://blog.csdn.net/kc58236582/article/details/51939322,已经分析的比较详细了,这里就不说了。


五、执行执行队列中的Action

执行命令主要是在main函数中的while循环中调用execute_one_command,因为执行队列会不断变化,所以需要在while循环中不断调用这个函数。

  1. while (true) {  
  2.     if (!waiting_for_exec) {  
  3.         execute_one_command();  
  4.         restart_processes();  
  5.     }  
  6.   
  7.     int timeout = -1;  
  8.     if (process_needs_restart) {  
  9.         timeout = (process_needs_restart - gettime()) * 1000;  
  10.         if (timeout < 0)  
  11.             timeout = 0;  
  12.     }  
  13.   
  14.     if (!action_queue_empty() || cur_action) {  
  15.         timeout = 0;  
  16.     }  
  17.   
  18.     bootchart_sample(&timeout);  
  19.   
  20.     epoll_event ev;  
  21.     int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));  
  22.     if (nr == -1) {  
  23.         ERROR("epoll_wait failed: %s\n", strerror(errno));  
  24.     } else if (nr == 1) {  
  25.         ((void (*)()) ev.data.ptr)();  
  26.     }  
  27. }  

我们来看下这个函数,比较简单先调用action_remove_queue_head函数,然后获取command,最后调用command的func回调函数。

  1. void execute_one_command() {  
  2.     Timer t;  
  3.   
  4.     char cmd_str[256] = "";  
  5.     char name_str[256] = "";  
  6.   
  7.     if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {  
  8.         cur_action = action_remove_queue_head();  
  9.         cur_command = NULL;  
  10.         if (!cur_action) {  
  11.             return;  
  12.         }  
  13.   
  14.         build_triggers_string(name_str, sizeof(name_str), cur_action);  
  15.   
  16.         INFO("processing action %p (%s)\n", cur_action, name_str);  
  17.         cur_command = get_first_command(cur_action);  
  18.     } else {  
  19.         cur_command = get_next_command(cur_action, cur_command);  
  20.     }  
  21.   
  22.     if (!cur_command) {  
  23.         return;  
  24.     }  
  25.   
  26.     int result = cur_command->func(cur_command->nargs, cur_command->args);  
  27.     if (klog_get_level() >= KLOG_INFO_LEVEL) {  
  28.         for (int i = 0; i < cur_command->nargs; i++) {  
  29.             strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));  
  30.             if (i < cur_command->nargs - 1) {  
  31.                 strlcat(cmd_str, " "sizeof(cmd_str));  
  32.             }  
  33.         }  
  34.         char source[256];  
  35.         if (cur_command->filename) {  
  36.             snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);  
  37.         } else {  
  38.             *source = '\0';  
  39.         }  
  40.         INFO("Command '%s' action=%s%s returned %d took %.2fs\n",  
  41.              cmd_str, cur_action ? name_str : "", source, result, t.duration());  
  42.     }  
  43. }  


六、kill 进程处理以及再次开启service进程

之前我们在分析signal_handler_init函数的时候没有详细说,现在说下这个函数。

  1. void signal_handler_init() {  
  2.     // Create a signalling mechanism for SIGCHLD.  
  3.     int s[2];  
  4.     if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {  
  5.         ERROR("socketpair failed: %s\n", strerror(errno));  
  6.         exit(1);  
  7.     }  
  8.   
  9.     signal_write_fd = s[0];  
  10.     signal_read_fd = s[1];  
  11.   
  12.     // Write to signal_write_fd if we catch SIGCHLD.  
  13.     struct sigaction act;  
  14.     memset(&act, 0, sizeof(act));  
  15.     act.sa_handler = SIGCHLD_handler;  
  16.     act.sa_flags = SA_NOCLDSTOP;  
  17.     sigaction(SIGCHLD, &act, 0);//子进程终结发给父进程的信号  
  18.   
  19.     reap_any_outstanding_children();  
  20.   
  21.     register_epoll_handler(signal_read_fd, handle_signal);  
  22. }  
我们先来看下信号的处理函数,SIGCHLD_handler就是往socketpair的一端写入数据
  1. static void SIGCHLD_handler(int) {  
  2.     if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {  
  3.         ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));  
  4.     }  
  5. }  
然后sockpair的另一端,注册到epoll中去,我们也来看下处理函数handle_signal,读取了sockpair中的内容后,调用了reap_any_outstanding_children函数,这个函数在signal_handler_init函数里面也调用了。
  1. static void handle_signal() {  
  2.     // Clear outstanding requests.  
  3.     char buf[32];  
  4.     read(signal_read_fd, buf, sizeof(buf));  
  5.   
  6.     reap_any_outstanding_children();  
  7. }  
我们来看下reap_any_outstanding_children函数,直接while循环调用了wait_for_one_process函数
  1. static void reap_any_outstanding_children() {  
  2.     while (wait_for_one_process()) {  
  3.     }  
  4. }  

我们再来看wait_for_one_process函数,先调用了waitpid方法,pid为-1,代表监听所有的子进程。WNOHANG代表不阻塞。当pid值返回0和-1时return false,直接while循环退出了,否则一直处理一个接着一个进程挂掉的信号。

  1. static bool wait_for_one_process() {  
  2.     int status;  
  3.     pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));//WNOHANG代表不阻塞  
  4.     if (pid == 0) {  
  5.         return false;  
  6.     } else if (pid == -1) {  
  7.         ERROR("waitpid failed: %s\n", strerror(errno));  
  8.         return false;  
  9.     }  
  10.   
  11.     service* svc = service_find_by_pid(pid);//找到service  
  12.   
  13.     std::string name;  
  14.     if (svc) {  
  15.         name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);  
  16.     } else {  
  17.         name = android::base::StringPrintf("Untracked pid %d", pid);  
  18.     }  
  19.   
  20.     NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());  
  21.   
  22.     if (!svc) {  
  23.         return true;//没找到service 直接结束处理下个进程信号  
  24.     }  
  25.   
  26.     // TODO: all the code from here down should be a member function on service.  
  27.   
  28.     if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {//如果不是oneshot 或者是restart的这种flag  
  29.         NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);  
  30.         kill(-pid, SIGKILL);//kill该进程群组所有的进程  
  31.     }  
  32.   
  33.     // Remove any sockets we may have created.去除socket  
  34.     for (socketinfo* si = svc->sockets; si; si = si->next) {  
  35.         char tmp[128];  
  36.         snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);  
  37.         unlink(tmp);  
  38.     }  
  39.   
  40.     if (svc->flags & SVC_EXEC) {  
  41.         INFO("SVC_EXEC pid %d finished...\n", svc->pid);  
  42.         waiting_for_exec = false;  
  43.         list_remove(&svc->slist);  
  44.         free(svc->name);  
  45.         free(svc);  
  46.         return true;  
  47.     }  
  48.   
  49.     svc->pid = 0;  
  50.     svc->flags &= (~SVC_RUNNING);//去除running的flag  
  51.   
  52.     // Oneshot processes go into the disabled state on exit,  
  53.     // except when manually restarted.  
  54.     if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {  
  55.         svc->flags |= SVC_DISABLED;//oneshot而且没有restart的flag,附上disabled的flag  
  56.     }  
  57.   
  58.     // Disabled and reset processes do not get restarted automatically.  
  59.     if (svc->flags & (SVC_DISABLED | SVC_RESET))  {//已经reset或者disabled直接结束  
  60.         svc->NotifyStateChange("stopped");  
  61.         return true;  
  62.     }  
  63.   
  64.     time_t now = gettime();  
  65.     if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {  
  66.         if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {  
  67.             if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {  
  68.                 ERROR("critical process '%s' exited %d times in %d minutes; "  
  69.                       "rebooting into recovery mode\n", svc->name,  
  70.                       CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);  
  71.                 android_reboot(ANDROID_RB_RESTART2, 0, "recovery");  
  72.                 return true;  
  73.             }  
  74.         } else {  
  75.             svc->time_crashed = now;  
  76.             svc->nr_crashed = 1;  
  77.         }  
  78.     }  
  79.   
  80.     svc->flags &= (~SVC_RESTART);  
  81.     svc->flags |= SVC_RESTARTING;// restarting代表重启中  
  82.   
  83.     // Execute all onrestart commands for this service.  
  84.     struct listnode* node;  
  85.     list_for_each(node, &svc->onrestart.commands) {//有onrestart的command命令的,重启的时候要先调用命令  
  86.         command* cmd = node_to_item(node, struct command, clist);  
  87.         cmd->func(cmd->nargs, cmd->args);  
  88.     }  
  89.     svc->NotifyStateChange("restarting");  
  90.     return true;  
  91. }  

这个函数主要将service的flags赋值,一般的进程被kill 之后最后会被附上SVC_RESTARTING这个flag,而且又onrestart的,先执行其command。对于已经是disabled和reset的service直接结束,对于是oneshot而且没有restart flag的service,直接附上disabled这个flag。

我们再来看看service的NotifyStateChange函数,主要是设置init.svc.(service的name)这个属性为这个service最新的状态。

  1. void service::NotifyStateChange(const char* new_state) {  
  2.     if (!properties_initialized()) {  
  3.         // If properties aren't available yet, we can't set them.  
  4.         return;  
  5.     }  
  6.   
  7.     if ((flags & SVC_EXEC) != 0) {  
  8.         // 'exec' commands don't have properties tracking their state.  
  9.         return;  
  10.     }  
  11.   
  12.     char prop_name[PROP_NAME_MAX];  
  13.     if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) {  
  14.         // If the property name would be too long, we can't set it.  
  15.         ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state);  
  16.         return;  
  17.     }  
  18.   
  19.     property_set(prop_name, new_state);  
  20. }  

下面我们需要再结合main函数中在while循环中调用的restart_processes函数

  1. static void restart_processes()  
  2. {  
  3.     process_needs_restart = 0;  
  4.     service_for_each_flags(SVC_RESTARTING,  
  5.                            restart_service_if_needed);  
  6. }  

结合service_for_each_flags,遍历所有的service,只要service的flags有SVC_RESTARTING的就调用restart_service_if_needed函数

  1. void service_for_each_flags(unsigned matchflags,  
  2.                             void (*func)(struct service *svc))  
  3. {  
  4.     struct listnode *node;  
  5.     struct service *svc;  
  6.     list_for_each(node, &service_list) {  
  7.         svc = node_to_item(node, struct service, slist);  
  8.         if (svc->flags & matchflags) {  
  9.             func(svc);  
  10.         }  
  11.     }  
  12. }  

在restart_service_if_needed函数中,会去除SVC_RESTARTING的flag,然后调用service_start启动进程。

  1. static void restart_service_if_needed(struct service *svc)  
  2. {  
  3.     time_t next_start_time = svc->time_started + 5;  
  4.   
  5.     if (next_start_time <= gettime()) {  
  6.         svc->flags &= (~SVC_RESTARTING);  
  7.         service_start(svc, NULL);  
  8.         return;  
  9.     }  
  10.   
  11.     if ((next_start_time < process_needs_restart) ||  
  12.         (process_needs_restart == 0)) {  
  13.         process_needs_restart = next_start_time;  
  14.     }  
  15. }  

所以普通的进程,使用kill的话,哪怕进程被kill了之后,还会被init进程启动的。我们再来看看service_start函数,先把一些flag清除

  1. void service_start(struct service *svc, const char *dynamic_args)  
  2. {  
  3.     // Starting a service removes it from the disabled or reset state and  
  4.     // immediately takes it out of the restarting state if it was in there.  
  5.     svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));//清除flag  
  6.     svc->time_started = 0;  
  7.   
  8.     // Running processes require no additional work --- if they're in the  
  9.     // process of exiting, we've ensured that they will immediately restart  
  10.     // on exit, unless they are ONESHOT.  
  11.     if (svc->flags & SVC_RUNNING) {//已经启动的service直接退出  
  12.         return;  
  13.     }  
  14.   
  15.     bool needs_console = (svc->flags & SVC_CONSOLE);//需要控制台的service  
  16.     if (needs_console && !have_console) {//是否有控制台,没有直接退出  
  17.         ERROR("service '%s' requires console\n", svc->name);  
  18.         svc->flags |= SVC_DISABLED;  
  19.         return;  
  20.     }  
  21.   
  22.     struct stat s;  
  23.     if (stat(svc->args[0], &s) != 0) {  
  24.         ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);  
  25.         svc->flags |= SVC_DISABLED;  
  26.         return;  
  27.     }  
  28.   
  29.     if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {  
  30.         ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",  
  31.                svc->args[0]);  
  32.         svc->flags |= SVC_DISABLED;  
  33.         return;  
  34.     }  

后面是selinux的相关,后面就直接fork进程,处理一些子进程的环境等。

  1. char* scon = NULL;  
  2. if (is_selinux_enabled() > 0) {  
  3. ......  
  4. }  
  5.   
  6. NOTICE("Starting service '%s'...\n", svc->name);  
  7.   
  8. pid_t pid = fork();  
  9. if (pid == 0) {  
  10.     ......  
  11.     _exit(127);  
  12. }  

最后设置下service的时间,pid,以及flags状态改成running,最后通知(设置属性)改成running了。

  1. if (pid < 0) {  
  2.     ERROR("failed to start '%s'\n", svc->name);  
  3.     svc->pid = 0;  
  4.     return;  
  5. }  
  6.   
  7. svc->time_started = gettime();  
  8. svc->pid = pid;  
  9. svc->flags |= SVC_RUNNING;  
  10.   
  11. if ((svc->flags & SVC_EXEC) != 0) {  
  12.     INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",  
  13.          svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,  
  14.          svc->seclabel ? : "default");  
  15.     waiting_for_exec = true;  
  16. }  
  17.   
  18. svc->NotifyStateChange("running");  

像普通的进程我们通过kill进程会被init再次启动,那怎样才能kill 这个进程,又不会被init进程启动呢,可以使用stop命令,我们看下do_stop函数。

  1. int do_stop(int nargs, char **args)  
  2. {  
  3.     struct service *svc;  
  4.     svc = service_find_by_name(args[1]);  
  5.     if (svc) {  
  6.         service_stop(svc);  
  7.     }  
  8.     return 0;  
  9. }  

调用了service_stop_or_reset只是flag为disabled

  1. void service_stop(struct service *svc)  
  2. {  
  3.     service_stop_or_reset(svc, SVC_DISABLED);  
  4. }  

service_stop_or_reset函数中,最后会kill整个进程组,而且因为把flag改成了disabled,在init中也不会启动进程了。

  1. static void service_stop_or_reset(struct service *svc, int how)  
  2. {  
  3.     /* The service is still SVC_RUNNING until its process exits, but if it has 
  4.      * already exited it shoudn't attempt a restart yet. */  
  5.     svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START);//清相关flag  
  6.   
  7.     if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {  
  8.         /* Hrm, an illegal flag.  Default to SVC_DISABLED */  
  9.         how = SVC_DISABLED;  
  10.     }  
  11.         /* if the service has not yet started, prevent 
  12.          * it from auto-starting with its class 
  13.          */  
  14.     if (how == SVC_RESET) {  
  15.         svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;// 看之前是否有disabled这个flag  
  16.     } else {  
  17.         svc->flags |= how;  
  18.     }  
  19.   
  20.     if (svc->pid) {  
  21.         NOTICE("Service '%s' is being killed...\n", svc->name);  
  22.         kill(-svc->pid, SIGKILL);//kill 整个进程组  
  23.         svc->NotifyStateChange("stopping");  
  24.     } else {  
  25.         svc->NotifyStateChange("stopped");  
  26.     }  
  27. }  

也可以自己调用start命令,最后通过do_start函数启动这个service。

  1. int do_start(int nargs, char **args)  
  2. {  
  3.     struct service *svc;  
  4.     svc = service_find_by_name(args[1]);  
  5.     if (svc) {  
  6.         service_start(svc, NULL);  
  7.     }  
  8.     return 0;  
  9. }  

再看下do_restart

  1. int do_restart(int nargs, char **args)  
  2. {  
  3.     struct service *svc;  
  4.     svc = service_find_by_name(args[1]);  
  5.     if (svc) {  
  6.         service_restart(svc);  
  7.     }  
  8.     return 0;  
  9. }  

在service_restart函数中,当是running状态,直接把它kill了,然后在init处理进程信号时,会把它的flag变成restarting,之后init进程会重启这个进程。其他的状态就直接启动service了。

  1. void service_restart(struct service *svc)  
  2. {  
  3.     if (svc->flags & SVC_RUNNING) {  
  4.         /* Stop, wait, then start the service. */  
  5.         service_stop_or_reset(svc, SVC_RESTART);  
  6.     } else if (!(svc->flags & SVC_RESTARTING)) {  
  7.         /* Just start the service since it's not running. */  
  8.         service_start(svc, NULL);  
  9.     } /* else: Service is restarting anyways. */  
  10. }  
至于stop start命令只能在init.rc中使用,但是我们可以通过ctl.start ctl.stop ctl.restart来达到这个目的。处理的话,之前在属性系统中已经分析过了。



普通对一个service命令处理只有stop start restart没有reset,而在class_reset class_stop class_start中有,我们来看看这些命令处理。

  1. int do_class_stop(int nargs, char **args)  
  2. {  
  3.     service_for_each_class(args[1], service_stop);  
  4.     return 0;  
  5. }  
  6.   
  7. int do_class_reset(int nargs, char **args)  
  8. {  
  9.     service_for_each_class(args[1], service_reset);  
  10.     return 0;  
  11. }  

再结合service_for_each_class函数,我们知道class_reset class_stop 只是遍历所有的service,看看其class是否满足,满足就调用service_stop 和 service_reset函数

  1. void service_for_each_class(const char *classname,  
  2.                             void (*func)(struct service *svc))  
  3. {  
  4.     struct listnode *node;  
  5.     struct service *svc;  
  6.     list_for_each(node, &service_list) {  
  7.         svc = node_to_item(node, struct service, slist);  
  8.         if (!strcmp(svc->classname, classname)) {  
  9.             func(svc);  
  10.         }  
  11.     }  
  12. }  
而我们再看看do_class_start有点不一样,遍历所有的service看看其class是否满足然后调用service_start_if_not_disabled函数
  1. int do_class_start(int nargs, char **args)  
  2. {  
  3.         /* Starting a class does not start services 
  4.          * which are explicitly disabled.  They must 
  5.          * be started individually. 
  6.          */  
  7.     service_for_each_class(args[1], service_start_if_not_disabled);  
  8.     return 0;  
  9. }  

看看service_start_if_not_disabled函数只有在flags不等于SVC_DISABLED的时候才会调用service_start函数。

  1. static void service_start_if_not_disabled(struct service *svc)  
  2. {  
  3.     if (!(svc->flags & SVC_DISABLED)) {  
  4.         service_start(svc, NULL);  
  5.     } else {  
  6.         svc->flags |= SVC_DISABLED_START;  
  7.     }  
  8. }  

这样如果调用class_stop再调用class_start也不能再次启动这些class的service了,只有启动那些之前调用的是reset的service。

class_stop了之后,就只能一个一个service调用start命令了。


原创地址  http://blog.csdn.net/kc58236582/article/details/52247547


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值