Android4.4之init

android是基于Linux内核的操作系统,所以系统的第一个用户空间进程是init进程, pid固定是1。

1.系统属性的设置

来看一下源码
/system/core/init/init.c -> main()

962int main(int argc, char **argv)
963{
964    int fd_count = 0;
965    struct pollfd ufds[4];
966    char *tmpdev;
967    char* debuggable;
968    char tmp[32];
969    int property_set_fd_init = 0;
970    int signal_fd_init = 0;
971    int keychord_fd_init = 0;
972    bool is_charger = false;
973
974    if (!strcmp(basename(argv[0]), "ueventd"))
975        return ueventd_main(argc, argv);
976
977    if (!strcmp(basename(argv[0]), "watchdogd"))
978        return watchdogd_main(argc, argv);
979    ...
980 }

首先简单介绍下ueventd和watchdogd
*ueventd是一个守护进程,主要作用是接收uevent来创建或删除/dev/xxx(设备节点)
watchdog也是一个用户空间的守护进程,可以定期对系统进行检测*
eventd和watchdog是init的软连接,就是说events和watchdog程序都是执行init.cpp->main()
在这里main函数的参数argv的第一个,argv[0]为自身运行目录路径和程序名,从而执行相应的代码
ok,简单了解,这里并不是关注的重点,接着往下看。

/system/core/init/init.c -> main()

989  int main(int argc, char** argv) {
997    ...
998    // Clear the umask.
999    umask(0);
1000   ...
1001 }

清理umask(进程的文件模型创建掩码),这个主要是文件权限的问题。
Linux内核给每一个进程都设定了一个掩码,当程序调用open、mkdir等函数创建文件或目录时,传入open的mode会先和掩码做运算,得到的文件mode,才是文件真正的mode。
譬如要创建一个目录,并设定它的文件权限为0777,mkdir(“testdir”, 0777)
但实际上写入的文件权限却未必是777,因为mkdir系统调用在创建testdir时,会将0777与当前进程的掩码(称为umask)运算,具体运算方法为 0777&~umask作为testdir的真正权限。因此上述init中首先调用umask(0)将进程掩码清0,这样调用open/mkdir等函数创建文件或目录时,传入的mode就会作为实际的值写入文件系统。
/system/core/init/init.c -> main()

982
983        /* Get the basic filesystem setup we need put
984         * together in the initramdisk on / and then we'll
985         * let the rc file figure out the rest.
986         */
987    mkdir("/dev", 0755);
988    mkdir("/proc", 0755);
989    mkdir("/sys", 0755);
990
991    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
992    mkdir("/dev/pts", 0755);
993    mkdir("/dev/socket", 0755);
994    mount("devpts", "/dev/pts", "devpts", 0, NULL);
995    mount("proc", "/proc", "proc", 0, NULL);
996    mount("sysfs", "/sys", "sysfs", 0, NULL);
997
998    /* indicate that booting is in progress to background fw loaders, etc */
999    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));

这里创建目录,并挂载内核文件系统。
/dev是内存文件系统,不会保存,每次开机都要重新创建。
检测/dev/.booting文件是否可读写和创建

接着看/system/core/init/init.c -> main()

1001        /* We must have some place other than / to create the
1002         * device nodes for kmsg and null, otherwise we won't
1003         * be able to remount / read-only later on.
1004         * Now that tmpfs is mounted on /dev, we can actually
1005         * talk to the outside world.
1006         */
1007    open_devnull_stdio();
1008    klog_init();

看一下open_devnull_stdio()
/system/core/init/init.c -> open_devnull_stdio()

378void open_devnull_stdio(void)
379{
380    int fd;
381    static const char *name = "/dev/__null__";
382    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
383        fd = open(name, O_RDWR);
384        unlink(name);
385        if (fd >= 0) {
386            dup2(fd, 0);
387            dup2(fd, 1);
388            dup2(fd, 2);
389            if (fd > 2) {
390                close(fd);
391            }
392            return;
393        }
394    }
395
396    exit(1);
397}

这里/sys/fs/selinux/null并不存在会打开失败,从而创建/dev/null目录项并标准输入(0),标准输出(1),标准错误文件描述符(2)定向到/dev/null,这样输入输出就没有了。
为什么要把stdio重定向到/dev/null设备呢?因为此时系统刚开始启动,用于接收init进程标准输出、标准错误的设备节点还不存在,所以直接把它们重定向到/dev/null了。

没有标准输出怎么打印日志?
接着看init.c->klog_init()

35void klog_init(void)
36{
37    static const char *name = "/dev/__kmsg__";
38
39    if (klog_fd >= 0) return; /* Already initialized */
40
41    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
42        klog_fd = open(name, O_WRONLY);
43        if (klog_fd < 0)
44                return;
45        fcntl(klog_fd, F_SETFD, FD_CLOEXEC);
46        unlink(name);
47    }
48}
49

并未初始化过,因此这里klog_fd为0,然后调用mknod创建设备节点文件/dev/kmsg.
其实这里的/dev/kmsg与/dev/kmsg是同一节点,他们的主设备号都为1,从设备号都为11。
同样上面的/dev/null与/dev/null也是同一节点,他们的主设备号都为1,从设备号都为3。
然后打开该文件将文件描述符保存到变量klog_fd中,接着调用fcntl(klog_fd, F_SETFD, FD_CLOEXEC),这里FD_CLOEXEC的值为1,表示当程序执行exec函数时本fd将被系统自动关闭,不传递给exec创建的新进程。
随后调用unlink来删除/dev/kmsg文件.
unlink功能描述:从文件系统中删除一个名称。如果名称是文件的最后一个连接,并且没有其它进程将文件打开,名称对应的文件会实际被删除。

继续看init.c

1007 int main(int argc, char **argv){
1008    ...
1009    property_init();
1010    ...
1011 }

这里是属性服务的初始化,可以参考这篇文章,讲的很详细了

接着看init.c->main();

1009 int main(int argc, char **argv){
1010    ...
1011    get_hardware_name(hardware, &revision);
1012
1013    process_kernel_cmdline();
1014    ...
1015 }

先看下init.c->get_hardware_name();

399void get_hardware_name(char *hardware, unsigned int *revision)
400{
401    char data[1024];
402    int fd, n;
403    char *x, *hw, *rev;
404
405    /* Hardware string was provided on kernel command line */
406    if (hardware[0])
407        return;
408
409    fd = open("/proc/cpuinfo", O_RDONLY);
410    if (fd < 0) return;
411
412    n = read(fd, data, 1023);
413    close(fd);
414    if (n < 0) return;
415
416    data[n] = 0;
417    hw = strstr(data, "\nHardware");
418    rev = strstr(data, "\nRevision");
419
420    if (hw) {
421        x = strstr(hw, ": ");
422        if (x) {
423            x += 2;
424            n = 0;
425            while (*x && *x != '\n') {
426                if (!isspace(*x))
427                    hardware[n++] = tolower(*x);
428                x++;
429                if (n == 31) break;
430            }
431            hardware[n] = 0;
432        }
433    }
434
435    if (rev) {
436        x = strstr(rev, ": ");
437        if (x) {
438            *revision = strtoul(x + 2, 0, 16);
439        }
440    }
441}

get_hardware_name()函数从”/proc/cpuinfo”文件读取相应字符串到data中
这里写图片描述
最终将SMDK4x12转成小写保存在init的hardware,将0008转成16进制保存在init的revision中
strstr()函数的功能:就是在第一个参数中查找第二个参数第一次出现的地址,将地址赋值给一个字符指针,接着就可以利用这个字符指针找到从这个地址开始往后的字符。
再看下init.c->process_kernel_cmdline();

770static void process_kernel_cmdline(void)
771{
772    /* don't expose the raw commandline to nonpriv processes */
773    chmod("/proc/cmdline", 0440);
774
775    /* first pass does the common stuff, and finds if we are in qemu.
776     * second pass is only necessary for qemu to export all kernel params
777     * as props.
778     */
779    import_kernel_cmdline(0, import_kernel_nv);
780    if (qemu[0])
781        import_kernel_cmdline(1, import_kernel_nv);
782
783    /* now propogate the info given on command line to internal variables
784     * used by init as well as the current required properties
785     */
786    export_kernel_boot_props();
787}

443void import_kernel_cmdline(int in_qemu,
444                           void (*import_kernel_nv)(char *name, int in_qemu))
445{
446    char cmdline[1024];
447    char *ptr;
448    int fd;
449
450    fd = open("/proc/cmdline", O_RDONLY);
451    if (fd >= 0) {
452        int n = read(fd, cmdline, 1023);
453        if (n < 0) n = 0;
454
455        /* get rid of trailing newline, it happens */
456        if (n > 0 && cmdline[n-1] == '\n') n--;
457
458        cmdline[n] = 0;
459        close(fd);
460    } else {
461        cmdline[0] = 0;
462    }
463
464    ptr = cmdline;
465    while (ptr && *ptr) {
466        char *x = strchr(ptr, ' ');
467        if (x != 0) *x++ = 0;
468        import_kernel_nv(ptr, in_qemu);
469        ptr = x;
470    }
471}

686static void import_kernel_nv(char *name, int for_emulator)
687{
688    char *value = strchr(name, '=');
689    int name_len = strlen(name);
690
691    if (value == 0) return;
692    *value++ = 0;
693    if (name_len == 0) return;
694
695    if (for_emulator) {
696        /* in the emulator, export any kernel option with the
697         * ro.kernel. prefix */
698        char buff[PROP_NAME_MAX];
699        int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
700
701        if (len < (int)sizeof(buff))
702            property_set( buff, value );
703        return;
704    }
705
706    if (!strcmp(name,"qemu")) {
707        strlcpy(qemu, value, sizeof(qemu));
708    } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
709        const char *boot_prop_name = name + 12;
710        char prop[PROP_NAME_MAX];
711        int cnt;
712
713        cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
714        if (cnt < PROP_NAME_MAX)
715            property_set(prop, value);
716    }
717}

首先将/proc/cmdline文件权限修改为只有root用户和root组用户可读写。
然后import_kernel_cmdline()函数的第一个参数in_qemu表示是否是虚拟机。
这里将/proc/cmdline的内容读进内存,然后以空格将整个字符串拆分成小的字符串
以我的samsung GT-N5100为例,/proc/cmdline内容为
这里写图片描述
然后分别调用import_kernel_nv()将拆分后的小字符串传入(注意这里的in_qemu为0,表示非虚拟机)。
import_kernel_nv()中将传入的字符串在以’=’拆分成name和value。此时for_emulator为0,所以跳到判断name是否等于’qemu’,又因为我/proc/cmdline中并没有qemu的name,所以并不会给qemu数组写入任何值,接着最终只会将格式为androidboot.XXX的name改成ro.boot.XXX,并将修改后的name和value写进android的系统属性中。
回到process_kernel_cmdline()由于前面并没有往qemu中写入任何值,所以qemu[0]为0,接着会执行export_kernel_boot_props()。
init.c->export_kernel_boot_props()

719static void export_kernel_boot_props(void)
720{
721    char tmp[PROP_VALUE_MAX];
722    int ret;
723    unsigned i;
724    struct {
725        const char *src_prop;
726        const char *dest_prop;
727        const char *def_val;
728    } prop_map[] = {
729        { "ro.boot.serialno", "ro.serialno", "", },
730        { "ro.boot.mode", "ro.bootmode", "unknown", },
731        { "ro.boot.baseband", "ro.baseband", "unknown", },
732        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
733    };
734
735    for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
736        ret = property_get(prop_map[i].src_prop, tmp);
737        if (ret > 0)
738            property_set(prop_map[i].dest_prop, tmp);
739        else
740            property_set(prop_map[i].dest_prop, prop_map[i].def_val);
741    }
742
743    ret = property_get("ro.boot.console", tmp);
744    if (ret)
745        strlcpy(console, tmp, sizeof(console));
746
747    /* save a copy for init's usage during boot */
748    property_get("ro.bootmode", tmp);
749    strlcpy(bootmode, tmp, sizeof(bootmode));
750
751    /* if this was given on kernel command line, override what we read
752     * before (e.g. from /proc/cpuinfo), if anything */
753    ret = property_get("ro.boot.hardware", tmp);
754    if (ret)
755        strlcpy(hardware, tmp, sizeof(hardware));
756    property_set("ro.hardware", hardware);
757
758    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
759    property_set("ro.revision", tmp);
760
761    /* TODO: these are obsolete. We should delete them */
762    if (!strcmp(bootmode,"factory"))
763        property_set("ro.factorytest", "1");
764    else if (!strcmp(bootmode,"factory2"))
765        property_set("ro.factorytest", "2");
766    else
767        property_set("ro.factorytest", "0");
768}

这里很简单,就是设置一些系统属性。接着往下看main()。
init.c->main()

1015    union selinux_callback cb;
1016    cb.func_log = klog_write;
1017    selinux_set_callback(SELINUX_CB_LOG, cb);
1018
1019    cb.func_audit = audit_callback;
1020    selinux_set_callback(SELINUX_CB_AUDIT, cb);
1021
1022    selinux_initialize();
1023    /* These directories were necessarily created before initial policy load
1024     * and therefore need their security context restored to the proper value.
1025     * This must happen before /dev is populated by ueventd.
1026     */
1027    restorecon("/dev");
1028    restorecon("/dev/socket");
1029    restorecon("/dev/__properties__");
1030    restorecon_recursive("/sys");
1031

这段代码是4.1之后添加的,有关SELinux的初始化和检查,暂时还没有研究过SELinux,而且这里与android启动的关系也不大,暂时先不看。
init.c->main()

1032    is_charger = !strcmp(bootmode, "charger");
1033
1034    INFO("property init\n");
1035    if (!is_charger)
1036        property_load_boot_defaults();

如果当前启动模式不是充电模式,将从/default.prop文件中加载默认的一些属性设置。

2. init.rc的解析

接下来好戏开始了,开始对init.rc进行解析
关于init.rc的具体语法可以看下/system/core/init/readme.txt,这里简单介绍下
init.rc主要定义两类结构:action与service
action
action是一组命令的集合

38Actions take the form of:
40on <trigger>
41   <command>
42   <command>
43   <command>

先说下command,command的格式如下
command-name <parament1> [parament2...]
<>表示必须存在的参数,[]表示可选参数
可以这样理解,每个command表示一个要执行的动作,对应一个函数当action被触发时,会依次执行每个command对应的函数。

trigger是一个触发器,表示什么时候执行这个action,在init.rc中主要有两种类型:
1普通型
这种类型的trigger直接以字符串表示,用以描述一个时间节点,当该trigger对应的action加入到全局action_list中后(该过程下面会讲述),在init.c->main()中可以通过相应的时间节点找出相应的action并将他们按序加入到一个待执行队列中稍后依次触发他们。
2属性型
trigger为property:<name>=<value>
这种类型的action只有在property name的值为value时才会被触发。

service
service表示一个可执行程序

51service <name> <pathname> [ <argument> ]*
52   <option>
53   <option>
54   ...

name字段为service的名字
pathname为该service对应的二进制程序的路径
随后是该程序的参数列表
option是该service的属性,具体option可以看下/system/core/init/readme.txt的描述。
这里提一下有一个class属性,

101class <name>
102   Specify a class name for the service.  All services in a
103   named class may be started or stopped together.  A service
104   is in the class "default" if one is not specified via the
105   class option.

该属性name在init.rc主要有core, main,charger三种,如果service没有定义该属性则name默认为default。他的作用将service归属于一个class组,然后可以在Actions中通过class_start、class_stop、class_reset等命令启动、停止、重启动相应class组中的所有service。

补充下除了action和service,还有一个import是用来导入其他init脚本文件的

好了,来具体看一下解析init.rc的代码
init.c->main()

1036int main(int argc, char **argv)
1037    ...
1038    INFO("reading config file\n");
1039    init_parse_config_file("/init.rc");
1040    ...
1041}

/system/core/init/init_parser.c

404int init_parse_config_file(const char *fn)
405{
406    char *data;
407    data = read_file(fn, 0);
408    if (!data) return -1;
409
410    parse_config(fn, data);
411    DUMP();
412    return 0;
413}

先将/init.rc的内容读入内存,然后调用parse_config()函数进行解析。

/system/core/init/init_parser.c

347static void parse_config(const char *fn, char *s)
348{
349    struct parse_state state;
350    struct listnode import_list;
351    struct listnode *node;
352    char *args[INIT_PARSER_MAXARGS];
353    int nargs;
354
355    nargs = 0;
356    state.filename = fn;
357    state.line = 0;
358    state.ptr = s;
359    state.nexttoken = 0;
360    state.parse_line = parse_line_no_op;
361
362    list_init(&import_list);
363    state.priv = &import_list;
364
365    for (;;) {
366        switch (next_token(&state)) {
367        case T_EOF:
368            state.parse_line(&state, 0, 0);
369            goto parser_done;
370        case T_NEWLINE:
371            state.line++;
372            if (nargs) {
373                int kw = lookup_keyword(args[0]);
374                if (kw_is(kw, SECTION)) {
375                    state.parse_line(&state, 0, 0);
376                    parse_new_section(&state, kw, nargs, args);
377                } else {
378                    state.parse_line(&state, nargs, args);
379                }
380                nargs = 0;
381            }
382            break;
383        case T_TEXT:
384            if (nargs < INIT_PARSER_MAXARGS) {
385                args[nargs++] = state.text;
386            }
387            break;
388        }
389    }
390
391parser_done:
392    list_for_each(node, &import_list) {
393         struct import *import = node_to_item(node, struct import, list);
394         int ret;
395
396         INFO("importing '%s'", import->filename);
397         ret = init_parse_config_file(import->filename);
398         if (ret)
399             ERROR("could not import file '%s' from '%s'\n",
400                   import->filename, fn);
401    }
402}

parse_config函数比较复杂主要做了
1. 解析字符串中所有的action,并将其链入action_list队列
2. 解析字符串中所有的service,并将他们链入service_list队列
3. 将import的文件读进内存,然后重复前面两步。

ok,在接下来具体分析之前先简单看一下有关的几个数据结构:
/system/core/init/init.h

26struct command
27{
28        /* list of commands in an action */
29    struct listnode clist;//用于将command链入一个双链队列
30
31    int (*func)(int nargs, char **args);//command语句所对应的函数指针
32    int nargs;//command语句所表示的命令对应的函数的参数个数
33    char *args[1];//命令对应的函数的参数
34};
35
36struct action {
37        /* node in list of all actions */
38    struct listnode alist;//用于将action链入action_list队列
39        /* node in the queue of pending actions */
40    struct listnode qlist;//用于将action链入action_queue队列
41        /* node in list of actions for a trigger */
42    struct listnode tlist;//也是用于将action链入队列,暂时在代码中看到
43
44    unsigned hash;//在目前init实现中未使用
45    const char *name;//action的trigger(用来标记action的执行时机)
46
47    struct listnode commands;//该action所有struct command链表
48    struct command *current;//在Actions执行时,存储当前正在被执行的struct command指针。
49};
51struct socketinfo {
52    struct socketinfo *next;
53    const char *name;
54    const char *type;
55    uid_t uid;
56    gid_t gid;
57    int perm;
58};
59
60struct svcenvinfo {
61    struct svcenvinfo *next;
62    const char *name;
63    const char *value;
64};
65
81struct service {
82        /* list of all services */
83    struct listnode slist;//用于将action链入service_list队列
84
85    const char *name;//service的name
86    const char *classname;//设置service的类别,感觉有点像开机启动service的优先级,默认的class名称为default,还有core、main、charger
87
88    unsigned flags;////位图变量,其各个位代表不同的servcie的属性(对应service中的option字段)
89    pid_t pid;////当service对应的程序执行时,存放其进程号 
90    time_t time_started;    //进程启动时间  
91    time_t time_crashed;    //存放第一次进程崩溃时间
92    int nr_crashed;       //存放进程崩溃次数  
93
94    uid_t uid;//该servcie对应进程的uid  
95    gid_t gid;//该service对应进程的gidinit_parse_config_file("/init.rc"); 
96    gid_t supp_gids[NR_SVC_SUPP_GIDS];//该service对应进程的附加群组id  
97    size_t nr_supp_gids;//该service所隶属的附件组的数目 
98
99    char *seclabel;//存放selinux所需要的security context  
100
101    struct socketinfo *sockets;// 为service创建的sockets 
102    struct svcenvinfo *envvars;// 为service设置的环境变量 
103
104    struct action onrestart;  //服务重启时,执行的命令
105
106    /* keycodes for triggering this service via /dev/keychord */
107    int *keycodes;//keycodes相关,init中未使用
108    int nkeycodes;
109    int keychord_id;
110
111    int ioprio_class;// io优先级  
112    int ioprio_pri;
113
114    int nargs;//对应service语句传入的参数数目  
115    /* "MUST BE AT THE END OF THE STRUCT" */
116    char *args[1];//存放service语句实际传入的参数,其长度将会被修正为nargs+1  
117}; /*     ^-------'args' MUST be at the end of this struct! */

另外还有一点先说明下,init_parse.c中定义了三个全局的双链队列:

/system/core/init/init_parser.c
39static list_declare(service_list);
40static list_declare(action_list);
41static list_declare(action_queue);

service_list是全局service链表,解析启动脚本过程中,service对应数据结构struct service将会挂载到这里。action_list是全局Actions链表,Actions对应数据结构struct action将会挂载到这里。至于action_queue在解析完毕之后,实际执行Actions时才会用到。

/system/core/init/parser.h

24struct parse_state
25{
26    char *ptr;
27    char *text;
28    int line;
29    int nexttoken;
30    void *context;
31    void (*parse_line)(struct parse_state *state, int nargs, char **args);
32    const char *filename;
33    void *priv;
34};

ptr是还未解析的字符串
text当前读出的内容
line当然就是行数了
nexttoken保存下一个要执行的操作(0或T_NEWLINE)
filename当前解析字符串所属文件的文件名
context当前正在解析的action或者service对象
parse_line解析command(parse_line_action())或者option(parse_line_service())的函数指针,或者如果如果当前并没有在解析一个action或service这个函数指针指向parse_line_no_op()是一个空实现。
state.priv指向一个链表,是当前文件import的所有文件。

/system/core/include/cutils/list.h

26struct listnode
27{
28    struct listnode *next;
29    struct listnode *prev;
30};

/system/core/libcutils/list.c

19void list_init(struct listnode *node)
20{
21    node->next = node;
22    node->prev = node;
23}

/system/core/libcutils/list.c

25void list_add_tail(struct listnode *head, struct listnode *item)
26{
27    item->next = head;
28    item->prev = head->prev;
29    head->prev->next = item;
30    head->prev = item;
31}

这里listnode的作用很明显也很简单,就是listnode的宿主数据结构可以通过listnode链入一个双向的队列中。

ok,来看代码:
/system/core/init/parser.c

68int next_token(struct parse_state *state)
69{
70    char *x = state->ptr;
71    char *s;
72
73    if (state->nexttoken) {
74        int t = state->nexttoken;
75        state->nexttoken = 0;
76        return t;
77    }
78
79    for (;;) {
80        switch (*x) {
81        case 0:
82            state->ptr = x;
83            return T_EOF;
84        case '\n':
85            x++;
86            state->ptr = x;
87            return T_NEWLINE;
88        case ' ':
89        case '\t':
90        case '\r':
91            x++;
92            continue;
93        case '#':
94            while (*x && (*x != '\n')) x++;
95            if (*x == '\n') {
96                state->ptr = x+1;
97                return T_NEWLINE;
98            } else {
99                state->ptr = x;
100                return T_EOF;
101            }
102        default:
103            goto text;
104        }
105    }
106
107textdone:
108    state->ptr = x;
109    *s = 0;
110    return T_TEXT;
111text:
112    state->text = s = x;
113textresume:
114    for (;;) {
115        switch (*x) {
116        case 0:
117            goto textdone;
118        case ' ':
119        case '\t':
120        case '\r':
121            x++;
122            goto textdone;
123        case '\n':
124            state->nexttoken = T_NEWLINE;
125            x++;
126            goto textdone;
127        case '"':
128            x++;
129            for (;;) {
130                switch (*x) {
131                case 0:
132                        /* unterminated quoted thing */
133                    state->ptr = x;
134                    return T_EOF;
135                case '"':
136                    x++;
137                    goto textresume;
138                default:
139                    *s++ = *x++;
140                }
141            }
142            break;
143        case '\\':
144            x++;
145            switch (*x) {
146            case 0:
147                goto textdone;
148            case 'n':
149                *s++ = '\n';
150                break;
151            case 'r':
152                *s++ = '\r';
153                break;
154            case 't':
155                *s++ = '\t';
156                break;
157            case '\\':
158                *s++ = '\\';
159                break;
160            case '\r':
161                    /* \ <cr> <lf> -> line continuation */
162                if (x[1] != '\n') {
163                    x++;
164                    continue;
165                }
166            case '\n':
167                    /* \ <lf> -> line continuation */
168                state->line++;
169                x++;
170                    /* eat any extra whitespace */
171                while((*x == ' ') || (*x == '\t')) x++;
172                continue;
173            default:
174                    /* unknown escape -- just copy */
175                *s++ = *x++;
176            }
177            continue;
178        default:
179            *s++ = *x++;
180        }
181    }
182    return T_EOF;
183}

next_token()的作用是忽略state->ptr(还未解析的字符串)中的注释,从中读出字符串并保存在state->text中直到遇到空格或换行。
当遇到空格时返回T_TEXT,当遇到换行时返回T_NEWLINE。
截取init.rc中部分内容为例:

12on early-init
13    # Set init and its forked children's oom_adj.
14    write /proc/1/oom_adj -16
15
16    # Set the security context for the init process.
17    # This should occur before anything else (e.g. ueventd) is started.
18    setcon u:r:init:s0
19
20    start ueventd

第一次执行next_token()读到on和early-init之间的空格,将on的开始地址保存在state->text中。state->ptr指针增加三位,从early-init开始。然后返回T_TEXT。
回到parse_config()中如果next_token()返回的是T_TEXT,而且nargs(此时为0)小于64,则将state->text以nargs为脚标存进args数组中。此时,args[0]存放的就是指向”on”开始字符串的首地址。
然后继续执行next_token(),这一次读到了第一行尾。并将early-init字符串的开始地址保存在state->text中。state->ptr指针增加11位,从第二行开始。将state->nexttoken 的值设置为T_NEWLINE;然后返回T_TEXT。
再回到parse_config()中这时next_token()返回的还是T_TEXT,所以将”early-init”开始字符串的首地址存进args[1]中。
这时再进入next_token(),因为state->nexttoken的值为T_NEWLINE(2),非0,所以先将state->nexttoken设为0,然后返回T_NEWLINE。
回到parse_config()中这时next_token()返回的就是T_NEWLINE了,所以先将行号增加1,然后判断nargs(此时为2)非0,接着调用lookup_keyword()并将args[0]也就是”on”的首地址传了过去。
看一下lookup_keyword()

79int lookup_keyword(const char *s)
80{
81    switch (*s++) {
80       ...
128    case 'o':
129        if (!strcmp(s, "n")) return K_on;
130        if (!strcmp(s, "neshot")) return K_oneshot;
131        if (!strcmp(s, "nrestart")) return K_onrestart;
132        break;
133     ...
169    return K_UNKNOWN;
170}

卧槽,K_on在哪,瞬间蒙逼了…看下/system/core/init/init_parser.c中的这块代码

58#include "keywords.h"
59
60#define KEYWORD(symbol, flags, nargs, func) \
61    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
62
63struct {
64    const char *name;
65    int (*func)(int nargs, char **args);
66    unsigned char nargs;
67    unsigned char flags;
68} keyword_info[KEYWORD_COUNT] = {
69    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
70#include "keywords.h"
71};
72#undef KEYWORD

这里的代码很巧妙,先理解下define中的#和##:
#是把参数字符串化;##是一个连接符号,用于把参数连在一起
这里keywords.h被include两遍,在第一次include keywords.h时KEYWORD宏被定义为下面形式

#define KEYWORD(symbol, flags, nargs, func) K_##symbol,

所以keywords.h中的enum会改为下面形式

43enum {
44    K_UNKNOWN,
46    K_capability,
47    K_chdir, 
49  ...
68    K_on,
69  ...
100    K_ioprio,
102    KEYWORD_COUNT,
103};

之后KEYWORD宏被undef了,在init_parser.c中重新被定义为

60#define KEYWORD(symbol, flags, nargs, func) \
61    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

所以在第二次include keywords.h时keywords.h就变成了

68 keyword_info[KEYWORD_COUNT] = {
69    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
70  [K_capability]={"capability",0,  0, 0x04},
71  ...
72  [K_on]={"on", 0,0,0x01},
73  ...
74};

ok,这下子就明朗的。lookup_keyword()返回枚举类型K_on,然后通过宏kw_is(kw, type) (keyword_info[kw].flags & (type))检查K_on是否是一个SECTION,结果为true,接着执行下面两句

375                    state.parse_line(&state, 0, 0);
376                    parse_new_section(&state, kw, nargs, args);

这时state的函数指针parse_line指向parse_line_no_op,是一个空操作,所以往下看parse_new_section()函数
/system/core/init/init_parser.c

320void parse_new_section(struct parse_state *state, int kw,
321                       int nargs, char **args)
322{
323    printf("[ %s %s ]\n", args[0],
324           nargs > 1 ? args[1] : "");
325    switch(kw) {
326    case K_service:
327        state->context = parse_service(state, nargs, args);
328        if (state->context) {
329            state->parse_line = parse_line_service;
330            return;
331        }
332        break;
333    case K_on:
334        state->context = parse_action(state, nargs, args);
335        if (state->context) {
336            state->parse_line = parse_line_action;
337            return;
338        }
339        break;
340    case K_import:
341        parse_import(state, nargs, args);
342        break;
343    }
344    state->parse_line = parse_line_no_op;
345}

parseaction()通过参数生成一个action结构,并把它保存在state->context中,然后将state->parse_line函数指针指向parse_line_service(),然后parse_new_section()就返回了。来具体看下parse_action():
/system/core/init/init_parser.c

821static void *parse_action(struct parse_state *state, int nargs, char **args)
822{
823    struct action *act;
824    if (nargs < 2) {
825        parse_error(state, "actions must have a trigger\n");
826        return 0;
827    }
828    if (nargs > 2) {
829        parse_error(state, "actions may not have extra parameters\n");
830        return 0;
831    }
832    act = calloc(1, sizeof(*act));
833    act->name = args[1];
834    list_init(&act->commands);
835    list_init(&act->qlist);
836    list_add_tail(&action_list, &act->alist);
837        /* XXX add to hash */
838    return act;
839}

这里一开始先检查了下action的参数个数是否符合语法规定,然后调用calloc()申请一个action节点,接着将trigger(这里也就是early-init)保存到了act->name。然后初始化了act->qlist和act->commands,并将该action链入action_list队列尾部,这几点不多说了。最后返回该action。
现在就返回到init_parser.c的->parse_config()了,然后把nargs置为0就开始了下一次for循环。略过#开头的注释行解析,next_token()函数解析完14 write /proc/1/oom_adj -16这一行后,nargs为3,args数组前三项分别保存了”write”,”/proc/1/oom_adj”,”-16”三个字符串。所以lookup_keyword()返回K_write, 接着kw_is(kw, SECTION)为false,因此执行state.parse_line(&state, nargs, args);。这里parse_line函数指针在之前parse_new_section()中指向了parse_line_action(),来具体看一下:
/system/core/init/init_parser.c

841static void parse_line_action(struct parse_state* state, int nargs, char **args)
842{
843    struct command *cmd;
844    struct action *act = state->context;
845    int (*func)(int nargs, char **args);
846    int kw, n;
847
848    if (nargs == 0) {
849        return;
850    }
851
852    kw = lookup_keyword(args[0]);
853    if (!kw_is(kw, COMMAND)) {
854        parse_error(state, "invalid command '%s'\n", args[0]);
855        return;
856    }
857
858    n = kw_nargs(kw);
859    if (nargs < n) {
860        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
861            n > 2 ? "arguments" : "argument");
862        return;
863    }
864    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
865    cmd->func = kw_func(kw);
866    cmd->nargs = nargs;
867    memcpy(cmd->args, args, sizeof(char*) * nargs);
868    list_add_tail(&act->commands, &cmd->clist);
869}

这里就是申请一个command节点,将cmd->func函数指针指向初始化keyword_info时定义的函数指针,这里是声明在keywords.h中的do_write()函数,然后将nargs保存在cmd->nargs中,将args数组保存在cmd->args中,最后将command链入act->commands的队列尾。
到这里基本上就能够明白action的解析过程了,解析完的action都被保存在了全局的action_list中,后面会根据trigger也就是act->name将action链入action_queue中然后依次执行,这个后面 action的执行 章节再看。

看完action再来看下service,以下面代码为例

408service ueventd /sbin/ueventd
409    class core
410    critical
411    seclabel u:r:ueventd:s0

类似action的解析,解析完第一行后args数组前三项分别存放了”service”,”evened”和”/sbin/ueventd”字符串的首地址。这里传入parse_line()的nargs为0,所以可以直接忽略这部调用,因为无论是在parse_line_service()还是在parse_line_action()中,当nargs为0都没有做任何事情。进入parse_new_section()中,此时kw=K_service,所以调用parse_service()解析service,并将解析的service保存到state->context,然后将state->parse_line函数指针指向parse_line_service(),接着就返回了。来看下parse_service():
/system/core/init/init_parser.c

613static void *parse_service(struct parse_state *state, int nargs, char **args)
614{
615    struct service *svc;
616    if (nargs < 3) {
617        parse_error(state, "services must have a name and a program\n");
618        return 0;
619    }
620    if (!valid_name(args[1])) {
621        parse_error(state, "invalid service name '%s'\n", args[1]);
622        return 0;
623    }
624
625    svc = service_find_by_name(args[1]);
626    if (svc) {
627        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
628        return 0;
629    }
630
631    nargs -= 2;
632    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
633    if (!svc) {
634        parse_error(state, "out of memory\n");
635        return 0;
636    }
637    svc->name = args[1];
638    svc->classname = "default";
639    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
640    svc->args[nargs] = 0;
641    svc->nargs = nargs;
642    svc->onrestart.name = "onrestart";
643    list_init(&svc->onrestart.commands);
644    list_add_tail(&service_list, &svc->slist);
645    return svc;
646}

service_find_by_name()是根据传过来的service name去全局serviced队列service_list中查找相同name的service,主要是为了避免重复定义同名service。接着调用calloc()申请一个service节点,将service name(这里是”ueventd”)赋值给svc->name,svc->classname默认设为”default”,nargs赋值给svc->nargs,初始化该service的onrestart.commands队列,最后将该service链入全局service_list,然后返回该service。
先在可以返回到parse_config()中继续调用next_token()读出内容了,当读完上面示例的第二行后,args数组前两项分别存放了”class”和”core”字符串的首地址,nargs为2。接着得到kw为K_class不是一个SECTION所以执行state.parse_line()也就是前面设置的parse_line_service():
/system/core/init/init_parser.c

648static void parse_line_service(struct parse_state *state, int nargs, char **args)
649{
650    struct service *svc = state->context;
651    struct command *cmd;
652    int i, kw, kw_nargs;
653
654    if (nargs == 0) {
655        return;
656    }
657
658    svc->ioprio_class = IoSchedClass_NONE;
659
660    kw = lookup_keyword(args[0]);
661    switch (kw) {
662    case K_capability:
663        break;
664    case K_class:
665        if (nargs != 2) {
666            parse_error(state, "class option requires a classname\n");
667        } else {
668            svc->classname = args[1];
669        }
670        break;
671    case K_console:
672        svc->flags |= SVC_CONSOLE;
673        break;
674    case K_disabled:
675        svc->flags |= SVC_DISABLED;
676        svc->flags |= SVC_RC_DISABLED;
677        break;
678    case K_ioprio:
679        if (nargs != 3) {
680            parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
681        } else {
682            svc->ioprio_pri = strtoul(args[2], 0, 8);
683
684            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
685                parse_error(state, "priority value must be range 0 - 7\n");
686                break;
687            }
688
689            if (!strcmp(args[1], "rt")) {
690                svc->ioprio_class = IoSchedClass_RT;
691            } else if (!strcmp(args[1], "be")) {
692                svc->ioprio_class = IoSchedClass_BE;
693            } else if (!strcmp(args[1], "idle")) {
694                svc->ioprio_class = IoSchedClass_IDLE;
695            } else {
696                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
697            }
698        }
699        break;
700    case K_group:
701        if (nargs < 2) {
702            parse_error(state, "group option requires a group id\n");
703        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
704            parse_error(state, "group option accepts at most %d supp. groups\n",
705                        NR_SVC_SUPP_GIDS);
706        } else {
707            int n;
708            svc->gid = decode_uid(args[1]);
709            for (n = 2; n < nargs; n++) {
710                svc->supp_gids[n-2] = decode_uid(args[n]);
711            }
712            svc->nr_supp_gids = n - 2;
713        }
714        break;
715    case K_keycodes:
716        if (nargs < 2) {
717            parse_error(state, "keycodes option requires atleast one keycode\n");
718        } else {
719            svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
720            if (!svc->keycodes) {
721                parse_error(state, "could not allocate keycodes\n");
722            } else {
723                svc->nkeycodes = nargs - 1;
724                for (i = 1; i < nargs; i++) {
725                    svc->keycodes[i - 1] = atoi(args[i]);
726                }
727            }
728        }
729        break;
730    case K_oneshot:
731        svc->flags |= SVC_ONESHOT;
732        break;
733    case K_onrestart:
734        nargs--;
735        args++;
736        kw = lookup_keyword(args[0]);
737        if (!kw_is(kw, COMMAND)) {
738            parse_error(state, "invalid command '%s'\n", args[0]);
739            break;
740        }
741        kw_nargs = kw_nargs(kw);
742        if (nargs < kw_nargs) {
743            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
744                kw_nargs > 2 ? "arguments" : "argument");
745            break;
746        }
747
748        cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
749        cmd->func = kw_func(kw);
750        cmd->nargs = nargs;
751        memcpy(cmd->args, args, sizeof(char*) * nargs);
752        list_add_tail(&svc->onrestart.commands, &cmd->clist);
753        break;
754    case K_critical:
755        svc->flags |= SVC_CRITICAL;
756        break;
757    case K_setenv: { /* name value */
758        struct svcenvinfo *ei;
759        if (nargs < 2) {
760            parse_error(state, "setenv option requires name and value arguments\n");
761            break;
762        }
763        ei = calloc(1, sizeof(*ei));
764        if (!ei) {
765            parse_error(state, "out of memory\n");
766            break;
767        }
768        ei->name = args[1];
769        ei->value = args[2];
770        ei->next = svc->envvars;
771        svc->envvars = ei;
772        break;
773    }
774    case K_socket: {/* name type perm [ uid gid ] */
775        struct socketinfo *si;
776        if (nargs < 4) {
777            parse_error(state, "socket option requires name, type, perm arguments\n");
778            break;
779        }
780        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
781                && strcmp(args[2],"seqpacket")) {
782            parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
783            break;
784        }
785        si = calloc(1, sizeof(*si));
786        if (!si) {
787            parse_error(state, "out of memory\n");
788            break;
789        }
790        si->name = args[1];
791        si->type = args[2];
792        si->perm = strtoul(args[3], 0, 8);
793        if (nargs > 4)
794            si->uid = decode_uid(args[4]);
795        if (nargs > 5)
796            si->gid = decode_uid(args[5]);
797        si->next = svc->sockets;
798        svc->sockets = si;
799        break;
800    }
801    case K_user:
802        if (nargs != 2) {
803            parse_error(state, "user option requires a user id\n");
804        } else {
805            svc->uid = decode_uid(args[1]);
806        }
807        break;
808    case K_seclabel:
809        if (nargs != 2) {
810            parse_error(state, "seclabel option requires a label string\n");
811        } else {
812            svc->seclabel = args[1];
813        }
814        break;
815
816    default:
817        parse_error(state, "invalid option '%s'\n", args[0]);
818    }
819}

这里就是根据kw的值设置前面创建的service的相应的属性或标记位,比较简单不多描述了。

init.rc中import的作用类似于include,就是导入其他的文件内容。

最后返回到init_parse_config_file()中,DUMP()是跟log有关的函数不需关注。

这样基本上init.rc的解析基本上就结束了。到此已经把init.rc及其导入文件中所有的action和service已经解析完了并分别链入了全局的action_list和service_list中,那么他们什么时候执行呢?带着问题,来看下下一节:action_queue的链入。

3. action_queue的链入

回到/system/core/init/init.c->main(),接下来就是从action_list中取出相应时间节点的action并将其链入action_queue中等待执行。看代码:
/system/core/init/init.c->main()

1039int main(int argc, char **argv){
1040    ...
1041    action_for_each_trigger("early-init", action_add_queue_tail);
1042
1043    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
1044    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
1045    queue_builtin_action(keychord_init_action, "keychord_init");
1046    queue_builtin_action(console_init_action, "console_init");
1047
1048    /* execute all the boot actions to get us started */
1049    action_for_each_trigger("init", action_add_queue_tail);
1050
1051    /* skip mounting filesystems in charger mode */
1052    if (!is_charger) {
1053        action_for_each_trigger("early-fs", action_add_queue_tail);
1054        action_for_each_trigger("fs", action_add_queue_tail);
1055        action_for_each_trigger("post-fs", action_add_queue_tail);
1056        action_for_each_trigger("post-fs-data", action_add_queue_tail);
1057    }
1058
1059    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
1060     * wasn't ready immediately after wait_for_coldboot_done
1061     */
1062    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
1063
1064    queue_builtin_action(property_service_init_action, "property_service_init");
1065    queue_builtin_action(signal_init_action, "signal_init");
1066    queue_builtin_action(check_startup_action, "check_startup");
1067
1068    if (is_charger) {
1069        action_for_each_trigger("charger", action_add_queue_tail);
1070    } else {
1071        action_for_each_trigger("early-boot", action_add_queue_tail);
1072        action_for_each_trigger("boot", action_add_queue_tail);
1073    }
1074
1075        /* run all property triggers based on current state of the properties */
1076    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
1077
1078
1079#if BOOTCHART
1080    queue_builtin_action(bootchart_init_action, "bootchart_init");
1081#endif
1082    ...
1083}

这里主要涉及两个函数action_for_each_trigger()和queue_builtin_action(),分别来看下
action_for_each_trigger()两个参数:一个为action的trigger;另一个为一个函数指针,init.c->main()中该函数指针传的都是action_add_queue_tail()。看下代码:

504void action_for_each_trigger(const char *trigger,
505                             void (*func)(struct action *act))
506{
507    struct listnode *node;
508    struct action *act;
509    list_for_each(node, &action_list) {
510        act = node_to_item(node, struct action, alist);
511        if (!strcmp(act->name, trigger)) {
512            func(act);
513        }
514    }
515}
588void action_add_queue_tail(struct action *act)
589{
590    if (list_empty(&act->qlist)) {
591        list_add_tail(&action_queue, &act->qlist);
592    }
593}

代码简单到掉眼泪,就是循环遍历全局队列action_list的每个节点,如果发现某个action的trigger与传过来的相同,就调用action_add_queue_tail()将该action链入action_queue队列的尾部。

在看下queue_builtin_action()函数的两个参数:第一个为一个函数指针,第二个看形参名就知道是action的trigger。来具体看下代码:

569void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
570{
571    struct action *act;
572    struct command *cmd;
573
574    act = calloc(1, sizeof(*act));
575    act->name = name;
576    list_init(&act->commands);
577    list_init(&act->qlist);
578
579    cmd = calloc(1, sizeof(*cmd));
580    cmd->func = func;
581    cmd->args[0] = name;
582    list_add_tail(&act->commands, &cmd->clist);
583
584    list_add_tail(&action_list, &act->alist);
585    action_add_queue_tail(act);
586}

代码也是非常之简单,主要做了三件事情:
1)构造一个触发器为name的struct action结构体,并创建一个struct command,对应函数为行参fund
2)将struct action添加到action_list链表末尾。
3)将struct action添加到action_queue链表末尾。

好的现在万事俱备,只差执行action_queue队列了。

4.action的执行

1083    for(;;) {
1084        int nr, i, timeout = -1;
1085
1086        execute_one_command();
1087        restart_processes();
1088        ...
1089    }

现在到了init.c->main()最后的for循环了。
execute_one_command()的作用就是执行action的一条command。
restart_processes()作用就是执行标志为SVC_RESTARTING的进程,fork一个新进程。
分别看下代码:

531void execute_one_command(void)
532{
533    int ret;
534
535    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
536        cur_action = action_remove_queue_head();
537        cur_command = NULL;
538        if (!cur_action)
539            return;
540        INFO("processing action %p (%s)\n", cur_action, cur_action->name);
541        cur_command = get_first_command(cur_action);
542    } else {
543        cur_command = get_next_command(cur_action, cur_command);
544    }
545
546    if (!cur_command)
547        return;
548
549    ret = cur_command->func(cur_command->nargs, cur_command->args);
550    INFO("command '%s' r=%d\n", cur_command->args[0], ret);
551}
595struct action *action_remove_queue_head(void)
596{
597    if (list_empty(&action_queue)) {
598        return 0;
599    } else {
600        struct listnode *node = list_head(&action_queue);
601        struct action *act = node_to_item(node, struct action, qlist);
602        list_remove(node);
603        list_init(node);
604        return act;
605    }
606}
514static struct command *get_next_command(struct action *act, struct command *cmd)
515{
516    struct listnode *node;
517    node = cmd->clist.next;
518    if (!node)
519        return NULL;
520    if (node == &act->commands)
521        return NULL;
522
523    return node_to_item(node, struct command, clist);
524}

execute_one_command()先判断当前action为空,或者当前command为空,或者当前command是action的最后一个command,就会会通过action_remove_queue_head()从action_queue头部取下一个listnode并把它转换成action结构,并将cur_action指针指向它。然后从该action的command队列中取出它的第一个listnode并把它转换成command结构,接着再将cur_command指针指向这个command。
如果前面一步的if判断结果为false,说明当前有一个action在执行,并且它的command还没有执行完,所以通过get_next_command()取出它的下一个command。
最后就执行了前面得到的command的func函数指针指向的函数。
再看下restart_processes():

434static void restart_processes()
435{
436    process_needs_restart = 0;
437    service_for_each_flags(SVC_RESTARTING,
438                           restart_service_if_needed);
439}
491void service_for_each_flags(unsigned matchflags,
492                            void (*func)(struct service *svc))
493{
494    struct listnode *node;
495    struct service *svc;
496    list_for_each(node, &service_list) {
497        svc = node_to_item(node, struct service, slist);
498        if (svc->flags & matchflags) {
499            func(svc);
500        }
501    }
502}
418static void restart_service_if_needed(struct service *svc)
419{
420    time_t next_start_time = svc->time_started + 5;
421
422    if (next_start_time <= gettime()) {
423        svc->flags &= (~SVC_RESTARTING);
424        service_start(svc, NULL);
425        return;
426    }
427
428    if ((next_start_time < process_needs_restart) ||
429        (process_needs_restart == 0)) {
430        process_needs_restart = next_start_time;
431    }
432}

service_for_each_flags()遍历service_list列表,找出那些flags中携带有SVC_RESTARTING标志的service节点,并执行restart_service_if_needed()。注意:为了防止出现service频繁重启,本次启动距离上次启动时间不得少于5秒。
service的具体启动service_start()函数后面有时间单独写篇文章介绍。
这样在这个无限循环中,action_queue里面每个action的每个command最终都会被执行,每个满足条件的需重启的service也会被启动。

for循环中还有这样一段代码:

1089        if (!property_set_fd_init && get_property_set_fd() > 0) {
1090            ufds[fd_count].fd = get_property_set_fd();
1091            ufds[fd_count].events = POLLIN;
1092            ufds[fd_count].revents = 0;
1093            fd_count++;
1094            property_set_fd_init = 1;
1095        }
1096        if (!signal_fd_init && get_signal_fd() > 0) {
1097            ufds[fd_count].fd = get_signal_fd();
1098            ufds[fd_count].events = POLLIN;
1099            ufds[fd_count].revents = 0;
1100            fd_count++;
1101            signal_fd_init = 1;
1102        }
1103        if (!keychord_fd_init && get_keychord_fd() > 0) {
1104            ufds[fd_count].fd = get_keychord_fd();
1105            ufds[fd_count].events = POLLIN;
1106            ufds[fd_count].revents = 0;
1107            fd_count++;
1108            keychord_fd_init = 1;
1109        }

这三个描述符分别是用来监听属性改变、子进程死亡和组合按键的。他们分别是在
queue_builtin_action(property_service_init_action, “property_service_init”);
queue_builtin_action(signal_init_action, “signal_init”);
queue_builtin_action(keychord_init_action, “keychord_init”);
中被创建。
组合按键关键字是keycodes,如果某个service中含有keycodes选项,那么当用户按下某种组合键时,该service将被重启。init.rc中并没有包含该关键字的service,不详细阐述了。
/system/core/init/init.c

789static int property_service_init_action(int nargs, char **args)
790{
791    /* read any property files on system or data and
792     * fire up the property service.  This must happen
793     * after the ro.foo properties are set above so
794     * that /data/local.prop cannot interfere with them.
795     */
796    start_property_service();
797    return 0;
798}

/system/core/init/property_service.c

581void start_property_service(void)
582{
583    int fd;
584
585    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
586    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
587    load_override_properties();
588    /* Read persistent properties after all default values have been loaded. */
589    load_persistent_properties();
590
591    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
592    if(fd < 0) return;
593    fcntl(fd, F_SETFD, FD_CLOEXEC);
594    fcntl(fd, F_SETFL, O_NONBLOCK);
595
596    listen(fd, 8);
597    property_set_fd = fd;
598}

创建一个socket用于进程间通信,对应的socket文件为/dev/socket/property_service,然后将该文件的文件描述符保存在property_set_fd中。这样当某个进程调用property_set来设置属性时,就会通过该socket给init进程发送一个消息,最终init会在poll中发现socket文件中有内容可读:

1131        nr = poll(ufds, fd_count, timeout);
1132        if (nr <= 0)
1133            continue;
1134
1135        for (i = 0; i < fd_count; i++) {
1136            if (ufds[i].revents == POLLIN) {
1137                if (ufds[i].fd == get_property_set_fd())
1138                    handle_property_set_fd();

在poll中如果描述符对应的文件有内容可读,则ufds[i].revents会被置为POLLIN。
接下来的调用流程为:
handle_property_set_fd() -> property_set()-> property_changed() -> queue_property_triggers()->action_add_queue_tail()
在queue_property_triggers函数中从全局action_list中匹配 property:=类型的Actions,如果属性数值满足,则将该Actions加入到action-queue中,这样该Actions中的command将会在之后的execute_one_command()被调用。

signal_init也是类似的流程
/system/core/init/init.c

799
800static int signal_init_action(int nargs, char **args)
801{
802    signal_init();
803    return 0;
804}

/system/core/init/signal_handler.c

131void signal_init(void)
132{
133    int s[2];
134
135    struct sigaction act;
136    memset(&act, 0, sizeof(act));
137    act.sa_handler = sigchld_handler;
138    act.sa_flags = SA_NOCLDSTOP;
139    sigaction(SIGCHLD, &act, 0);
140
141    /* create a signalling mechanism for the sigchld handler */
142    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
143        signal_fd = s[0];
144        signal_recv_fd = s[1];
145        fcntl(s[0], F_SETFD, FD_CLOEXEC);
146        fcntl(s[0], F_SETFL, O_NONBLOCK);
147        fcntl(s[1], F_SETFD, FD_CLOEXEC);
148        fcntl(s[1], F_SETFL, O_NONBLOCK);
149    }
150
151    handle_signal();
152}
36static void sigchld_handler(int s)
37{
38    write(signal_fd, &s, 1);
39}

linux系统中子进程死亡时会向父进程发送一个SIGCHLD信号,sigaction(SIGCHLD, &act, 0)就是注册收到子进程SIGCHLD信号后的回调方法。所以每当有子进程终止时,系统就会回调sigchld_handler()函数。sigchld_handler()非常简单,就是是向signal_init()中创建的“socket对”里的signal_fd写数据,然后init.c在poll函数中就能通过“socket对”的另一个句柄signal_recv_fd得到所写的数据。然后调用handle_signal():

1131        nr = poll(ufds, fd_count, timeout);
1132        if (nr <= 0)
1133            continue;
1134
1135        for (i = 0; i < fd_count; i++) {
1136            if (ufds[i].revents == POLLIN) {
1137                if (ufds[i].fd == get_property_set_fd())
1138                    handle_property_set_fd();
1139                else if (ufds[i].fd == get_keychord_fd())
1140                    handle_keychord();
1141                else if (ufds[i].fd == get_signal_fd())
1142                    handle_signal();
1143            }
1144        }

/system/core/init/signal_handler.c

121void handle_signal(void)
122{
123    char tmp[32];
124
125    /* we got a SIGCHLD - reap and restart as needed */
126    read(signal_recv_fd, tmp, sizeof(tmp));
127    while (!wait_for_one_process(0))
128        ;
129}
44static int wait_for_one_process(int block)
45{
46    pid_t pid;
47    int status;
48    struct service *svc;
49    struct socketinfo *si;
50    time_t now;
51    struct listnode *node;
52    struct command *cmd;
53
54    while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
55    if (pid <= 0) return -1;
56    INFO("waitpid returned pid %d, status = %08x\n", pid, status);
57
58    svc = service_find_by_pid(pid);
59    if (!svc) {
60        ERROR("untracked pid %d exited\n", pid);
61        return 0;
62    }
63
64    NOTICE("process '%s', pid %d exited\n", svc->name, pid);
65
66    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
67        kill(-pid, SIGKILL);
68        NOTICE("process '%s' killing any children in process group\n", svc->name);
69    }
70
71    /* remove any sockets we may have created */
72    for (si = svc->sockets; si; si = si->next) {
73        char tmp[128];
74        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
75        unlink(tmp);
76    }
77
78    svc->pid = 0;
79    svc->flags &= (~SVC_RUNNING);
80
81        /* oneshot processes go into the disabled state on exit,
82         * except when manually restarted. */
83    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
84        svc->flags |= SVC_DISABLED;
85    }
86
87        /* disabled and reset processes do not get restarted automatically */
88    if (svc->flags & (SVC_DISABLED | SVC_RESET) )  {
89        notify_service_state(svc->name, "stopped");
90        return 0;
91    }
92
93    now = gettime();
94    if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
95        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
96            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
97                ERROR("critical process '%s' exited %d times in %d minutes; "
98                      "rebooting into recovery mode\n", svc->name,
99                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
100                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
101                return 0;
102            }
103        } else {
104            svc->time_crashed = now;
105            svc->nr_crashed = 1;
106        }
107    }
108
109    svc->flags &= (~SVC_RESTART);
110    svc->flags |= SVC_RESTARTING;
111
112    /* Execute all onrestart commands for this service. */
113    list_for_each(node, &svc->onrestart.commands) {
114        cmd = node_to_item(node, struct command, clist);
115        cmd->func(cmd->nargs, cmd->args);
116    }
117    notify_service_state(svc->name, "restarting");
118    return 0;
119}

wait_for_one_process()中先找到挂掉的进程对应的service节点,并将该节点的flags添加SVC_RESTARTING标记,然后执行这个service节点中所有onrestart选项对应的动作。注意这里并没有直接重启service,而是在init.c->main()中的下一次for循环中,通过调用restart_processes()找到flags有SVC_RESTARTING标记的service重启他们。

本来想写的尽量详细,后来。。后来我就学乖了。。

参考博客:
http://blog.csdn.net/prife/article/details/41777245
http://blog.csdn.net/chenyufei1013/article/details/7927923

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值