Android6.0系统启动流程分析一:init进程

来源:互联网 发布:编程就业方向 编辑:程序博客网 时间:2024/05/20 08:25

文章来源:http://blog.csdn.net/u011913612/article/details/53204253?ref=myread


到了Android6.0,Init进程使用c++来写了,不过没有关系,它和c写的init没有太大的区别。
Init进程的入口代码是:system\core\init\init.cpp
main函数:

int main(int argc, char** argv) {    if (!strcmp(basename(argv[0]), "ueventd")) {        return ueventd_main(argc, argv);    }    if (!strcmp(basename(argv[0]), "watchdogd")) {        return watchdogd_main(argc, argv);    }    // Clear the umask.    umask(0);    add_environment("PATH", _PATH_DEFPATH);    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);    // Get the basic filesystem setup we need put together in the initramdisk    // on / and then we'll let the rc file figure out the rest.    if (is_first_stage) {        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");        mkdir("/dev/pts", 0755);        mkdir("/dev/socket", 0755);        mount("devpts", "/dev/pts", "devpts", 0, NULL);        mount("proc", "/proc", "proc", 0, NULL);        mount("sysfs", "/sys", "sysfs", 0, NULL);    }    // We must have some place other than / to create the device nodes for    // kmsg and null, otherwise we won't be able to remount / read-only    // later on. Now that tmpfs is mounted on /dev, we can actually talk    // to the outside world.    open_devnull_stdio();    klog_init();    klog_set_level(KLOG_NOTICE_LEVEL);    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");    if (!is_first_stage) {        // Indicate that booting is in progress to background fw loaders, etc.        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));        property_init();        // If arguments are passed both on the command line and in DT,        // properties set in DT always have priority over the command-line ones.        process_kernel_dt();        process_kernel_cmdline();        // Propogate the kernel variables to internal variables        // used by init as well as the current required properties.        export_kernel_boot_props();    }    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.    selinux_initialize(is_first_stage);    // If we're in the kernel domain, re-exec init to transition to the init domain now    // that the SELinux policy has been loaded.    if (is_first_stage) {        if (restorecon("/init") == -1) {            ERROR("restorecon failed: %s\n", strerror(errno));            security_failure();        }        char* path = argv[0];        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };        if (execv(path, args) == -1) {            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));            security_failure();        }    }    // These directories were necessarily created before initial policy load    // and therefore need their security context restored to the proper value.    // This must happen before /dev is populated by ueventd.    INFO("Running restorecon...\n");    restorecon("/dev");    restorecon("/dev/socket");    restorecon("/dev/__properties__");    restorecon_recursive("/sys");    epoll_fd = epoll_create1(EPOLL_CLOEXEC);    if (epoll_fd == -1) {        ERROR("epoll_create1 failed: %s\n", strerror(errno));        exit(1);    }    signal_handler_init();    property_load_boot_defaults();    start_property_service();    init_parse_config_file("/init.rc");    action_for_each_trigger("early-init", action_add_queue_tail);    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");    // ... so that we can start queuing up actions that require stuff from /dev.    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");    queue_builtin_action(keychord_init_action, "keychord_init");    queue_builtin_action(console_init_action, "console_init");    // Trigger all the boot actions to get us started.    action_for_each_trigger("init", action_add_queue_tail);    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random    // wasn't ready immediately after wait_for_coldboot_done    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");    // Don't mount filesystems or start core system services in charger mode.    char bootmode[PROP_VALUE_MAX];    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {        action_for_each_trigger("charger", action_add_queue_tail);    } else {        action_for_each_trigger("late-init", action_add_queue_tail);    }    // Run all property triggers based on current state of the properties.    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");    while (true) {        if (!waiting_for_exec) {            execute_one_command();            restart_processes();        }        int timeout = -1;        if (process_needs_restart) {            timeout = (process_needs_restart - gettime()) * 1000;            if (timeout < 0)                timeout = 0;        }        if (!action_queue_empty() || cur_action) {            timeout = 0;        }        bootchart_sample(&timeout);        epoll_event ev;        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));        if (nr == -1) {            ERROR("epoll_wait failed: %s\n", strerror(errno));        } else if (nr == 1) {            ((void (*)()) ev.data.ptr)();        }    }    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152

1.这个函数是否往下执行取决于传入的参数,如果第0个参数的basename为ueventd,则执行ueventd_main(argc, argv);如果basename为watchdogd_main,则执行watchdogd_main(argc, argv);只有basename不为这二者时,才会继续往下执行。
2.如果argv[1]不为”–second-stage”或者只有一个参数的话,那么is_first_stage就为true,就会创建/dev/pts和”/dev/socket”两个设备文件节点,并挂载一个文件系统。可以看出来init进程分两个阶段,不同的阶段有不同的行为。具体的内涵鄙人还没搞明白。
3.启动属性服务。创建一个socket,并在之后的死循环中监听这个socket返回的文件描述符。
3.解析init.rc。这个过程也是我最感兴趣的,也是最重要的复杂的。
4.对各个阶段的action排序。
5.进入死循环。
6.第一次进入死循环后,action_queue里面有很多时间,因此需要不断调用execute_one_command来执行命令。此时,action_queue_empty为假,timeout 为0,init线程不会在epoll_wait方法中休眠,因为设置的timeout=0哦,这一点曾一度困扰了我。
7.所有的命令执行完后,init进程进入休眠,监听property_set_fd和signal_read_fd两个文件描述符,一点他们有事件过来,立刻被唤醒,进而做事件处理。

init.rc梳理

在我们分析init.rc的解析过程之前,我们还需要先对init.rc有个基本的认识。
先看一张我根据理解绘制的图:
这里写图片描述
从图来看,init.rc主要有section组成,section由on,import,section三个关键字标示。其中on标示的section叫做action。
import就不用说了,和C语言中的include功能有点类似。
service格式如下

service <name> <pathname> [ <argument> ]*     <option>     <option>     ...  
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

action后面会跟一个触发器,然后另起一行开始放置命令(command),格式如下:

on <trigger>     <command>     <command>     <command>  
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

跟在service后面的是option,跟在action后面的是command.command都会对应一个处理函数,定义在keywords.h中:

...    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)    KEYWORD(mount_all,   COMMAND, 1, do_mount_all)    KEYWORD(mount,       COMMAND, 3, do_mount)    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

命名也是很有规则的。比如mkdir,对应的函数就是do_mkdir。我们看看do_mkdir做了什么:

int do_mkdir(int nargs, char **args){    mode_t mode = 0755;    int ret;    /* mkdir <path> [mode] [owner] [group] */    if (nargs >= 3) {        mode = strtoul(args[2], 0, 8);    }    ret = make_dir(args[1], mode);    /* chmod in case the directory already exists */    if (ret == -1 && errno == EEXIST) {        ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);    }    if (ret == -1) {        return -errno;    }    if (nargs >= 4) {        uid_t uid = decode_uid(args[3]);        gid_t gid = -1;        if (nargs == 5) {            gid = decode_uid(args[4]);        }        if (lchown(args[1], uid, gid) == -1) {            return -errno;        }        /* chown may have cleared S_ISUID and S_ISGID, chmod again */        if (mode & (S_ISUID | S_ISGID)) {            ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);            if (ret == -1) {                return -errno;            }        }    }    return e4crypt_set_directory_policy(args[1]);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

其实就是调用了make_dir并做了一些权限等方面的操作。所以,跟在action后面的命令并不能随随便便乱加,而是要确保这个命令被定义了,不然就会出错。

init.rc的解析过程(以import为例)

因为init.rc的第一行代码就是Import语句。万事开头难,只要我们理清了第一行的解析过程,后面行的解析分析起来就不怎么费劲了。所以下面我们主要看看init.rc中第一行的解析过程。
init.tc的解析函数为:init_parse_config_file

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

这个函数把/init.rc中的内容读出来,并让data这个string类型的变量指向它。
把读出来的data传递给parse_config函数做真正的解析工作。parse_config函数如下:

static void parse_config(const char *fn, const std::string& data){    char *args[UEVENTD_PARSER_MAXARGS];    int nargs = 0;    parse_state state;    state.filename = fn;    state.line = 1;    state.ptr = strdup(data.c_str());  // TODO: fix this code!    state.nexttoken = 0;    state.parse_line = parse_line_no_op;    for (;;) {        int token = next_token(&state);        switch (token) {        case T_EOF:            parse_line(&state, args, nargs);            return;        case T_NEWLINE:            if (nargs) {                parse_line(&state, args, nargs);                nargs = 0;            }            state.line++;            break;        case T_TEXT:            if (nargs < UEVENTD_PARSER_MAXARGS) {                args[nargs++] = state.text;            }            break;        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

我看到这个函数的时候,我想起了xml解析方法之一的pull解析,感觉挺像的。每次循环都会找到一个token,token就是一个特定的符号,然后根据这个toke做不同的处理。这里使用到了parse_state结构,启动以如下:

struct parse_state{    char *ptr;    char *text;    int line;    int nexttoken;    void *context;    void (*parse_line)(struct parse_state *state, int nargs, char **args);    const char *filename;    void *priv;};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这个就够中:ptr执行init.rc字符流的,text后面会用到,用来保存参数,line当然就是行数了,nexttoken保存下一个token,filename保存init.rc的文件描述符,filename当然是/init.rc了.parse_line是一个函数指针。context暂时没明白…state.priv 指向Import的一个文件链表。
我们打开Init.rc看看,从头分析它的解析过程。

import /init.environ.rcimport /init.usb.rcimport /init.${ro.hardware}.rcimport /init.${ro.zygote}.rcimport /init.trace.rc...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

init.rc前面几行都是import语句,我们看看一开始的解析流程。
这个时候,parse_satate的状态为:

    state.filename = fn;    state.line = 1;    state.ptr = strdup(data.c_str());  // TODO: fix this code!    state.nexttoken = 0;    state.parse_line = parse_line_no_op;        list_init(&import_list);    state.priv = &import_list;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

step 1.第一次循环

然后进入死循环,第一次调用next_token函数:

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

这时候,init.rc中的第一个符号应该是i(impor,省去空格),所以next_token直接进入到text:标签执行,执行的结果是state->text = s = ‘i’;然后继续执行textresume:标签后面的内容:
标签后面的for死循环中,发现第一个字符是i,于是执行default分支:*s++ = *x++;这样直到import的t被检测完以后,在下一次循环变得到一个空格,于是执行x++,并goto textdone.。textdown执行完后函数返回,返回后,state->ptr 指向’/’符号 。*s = 0;意味着state.text就是字符串“import”,因为0就是字符串结束符了。注意返回值为T_TEXT。这个时候执行parse_config函数中的case T_TEXT:分支。

        case T_TEXT:            if (nargs < INIT_PARSER_MAXARGS) {                args[nargs++] = state.text;            }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

这个时候,nargs为0,state.text位import,于是args数组的第0项就存了”import”字符串了。然后nargs++,也就是等于1了。然后进入下次循环。

step 2.第二次循环

第二次循环再次调用next_token函数,这次state->ptr=’\’,这我们分析过了。因此next_token函数不断执行defaulty分支,最终state.text = “/init.environ.rc”,返回类型还是T_TEXT。于是和之前一样,args[1]=”/init.environ.rc”,nargs=2。

step 3.第三次循环

这个时候一行结束,parse_config函数进入case T_NEWLINE:分支。
这个分支中,首先执行lookup_keyword函数,从名字来看是查找关键字。肯定就是import了,它肯定是关键字。不信请看代码:

static int lookup_keyword(const char *s){    switch (*s++) {    case 'b':        if (!strcmp(s, "ootchart_init")) return K_bootchart_init;        break;    case 'c':        if (!strcmp(s, "opy")) return K_copy;        if (!strcmp(s, "lass")) return K_class;        if (!strcmp(s, "lass_start")) return K_class_start;        if (!strcmp(s, "lass_stop")) return K_class_stop;        if (!strcmp(s, "lass_reset")) return K_class_reset;        if (!strcmp(s, "onsole")) return K_console;        if (!strcmp(s, "hown")) return K_chown;        if (!strcmp(s, "hmod")) return K_chmod;        if (!strcmp(s, "ritical")) return K_critical;        break;    case 'd':        if (!strcmp(s, "isabled")) return K_disabled;        if (!strcmp(s, "omainname")) return K_domainname;        break;    case 'e':        if (!strcmp(s, "nable")) return K_enable;        if (!strcmp(s, "xec")) return K_exec;        if (!strcmp(s, "xport")) return K_export;        break;    case 'g':        if (!strcmp(s, "roup")) return K_group;        break;    case 'h':        if (!strcmp(s, "ostname")) return K_hostname;        break;    case 'i':        if (!strcmp(s, "oprio")) return K_ioprio;        if (!strcmp(s, "fup")) return K_ifup;        if (!strcmp(s, "nsmod")) return K_insmod;        if (!strcmp(s, "mport")) return K_import;        if (!strcmp(s, "nstallkey")) return K_installkey;        break;    case 'k':        if (!strcmp(s, "eycodes")) return K_keycodes;        break;    case 'l':        if (!strcmp(s, "oglevel")) return K_loglevel;        if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;        if (!strcmp(s, "oad_all_props")) return K_load_all_props;        break;    case 'm':        if (!strcmp(s, "kdir")) return K_mkdir;        if (!strcmp(s, "ount_all")) return K_mount_all;        if (!strcmp(s, "ount")) return K_mount;        break;    case 'o':        if (!strcmp(s, "n")) return K_on;        if (!strcmp(s, "neshot")) return K_oneshot;        if (!strcmp(s, "nrestart")) return K_onrestart;        break;    case 'p':        if (!strcmp(s, "owerctl")) return K_powerctl;        break;    case 'r':        if (!strcmp(s, "estart")) return K_restart;        if (!strcmp(s, "estorecon")) return K_restorecon;        if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;        if (!strcmp(s, "mdir")) return K_rmdir;        if (!strcmp(s, "m")) return K_rm;        break;    case 's':        if (!strcmp(s, "eclabel")) return K_seclabel;        if (!strcmp(s, "ervice")) return K_service;        if (!strcmp(s, "etenv")) return K_setenv;        if (!strcmp(s, "etprop")) return K_setprop;        if (!strcmp(s, "etrlimit")) return K_setrlimit;        if (!strcmp(s, "ocket")) return K_socket;        if (!strcmp(s, "tart")) return K_start;        if (!strcmp(s, "top")) return K_stop;        if (!strcmp(s, "wapon_all")) return K_swapon_all;        if (!strcmp(s, "ymlink")) return K_symlink;        if (!strcmp(s, "ysclktz")) return K_sysclktz;        break;    case 't':        if (!strcmp(s, "rigger")) return K_trigger;        break;    case 'u':        if (!strcmp(s, "ser")) return K_user;        break;    case 'v':        if (!strcmp(s, "erity_load_state")) return K_verity_load_state;        if (!strcmp(s, "erity_update_state")) return K_verity_update_state;        break;    case 'w':        if (!strcmp(s, "rite")) return K_write;        if (!strcmp(s, "ritepid")) return K_writepid;        if (!strcmp(s, "ait")) return K_wait;        break;    }    return K_UNKNOWN;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

调用这个函数的时候,我们传入的参数args[0]=”import”.显而易见该函数返回K_import。它是一个整数。返回以后使用kw_is函数看他是不是一个Section。当然是一个section了,import也是一个section。不信看代码:

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

keyword_info定义在system/core/init/keywords.h中:

    ...    KEYWORD(group,       OPTION,  0, 0)    KEYWORD(hostname,    COMMAND, 1, do_hostname)    KEYWORD(ifup,        COMMAND, 1, do_ifup)    KEYWORD(import,      SECTION, 1, 0)    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

截取含有import的一部分代码,后面SECTION已经表明它是个Section了。KEYWORD自后一个参数是这个关键字对应的处理函数。比如这其中的hostname。如果你在init.rc中使用hostname 关键字,那么最终会调用do_hostname函数来处理。
既然import是一个section。那么parce_config就会调用state.parse_line函数,这里是一个函数指针,其实调用的是parse_line_no_op,不记得回去看下state的初始就知道了。

static void parse_line_no_op(struct parse_state*, int, char**) {}
  • 1
  • 2
  • 1
  • 2

这个函数是空的。接下来调用parse_new_section函数:

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

我们当然是执行case K_import:分支了,想都不用想。所以接下来执行parse_import方法:

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

这个函数首先使用expand_props方法对args[1]也就是“/init.environ.rc”做进一步处理。这个函数如下:

int expand_props(char *dst, const char *src, int dst_size){    char *dst_ptr = dst;    const char *src_ptr = src;    int ret = 0;    int left = dst_size - 1;    if (!src || !dst || dst_size == 0)        return -1;    /* - variables can either be $x.y or ${x.y}, in case they are only part     *   of the string.     * - will accept $$ as a literal $.     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,     *   bad things will happen     */    while (*src_ptr && left > 0) {        char *c;        char prop[PROP_NAME_MAX + 1];        char prop_val[PROP_VALUE_MAX];        int prop_len = 0;        int prop_val_len;        c = strchr(src_ptr, '$');        if (!c) {            while (left-- > 0 && *src_ptr)                *(dst_ptr++) = *(src_ptr++);            break;        }        ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

可以看出,这个函数的作用是拓展args[1].这里不需要拓展,因为我们的args[1]=”/init.environ.rc”没有$符号,所以直接就跳出循环了。这里应该是对那些有包含变量的字符串,把变量的内容展开。
然后构建了一个import结构体。import中的filename项赋值为”/init.environ.rc”.并把它加入到import_list链表中。
import定义如下:

struct import {    struct listnode list;    const char *filename;};
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

这样,第一行就分析完了,从而我们也彻底明白了怎么解析一个import 的section。
只要能看懂一个,其他的就简单了。因为,他们都是类似的。

service的解析与启动

service的解析

和import解析过程类似,遇到service关键字后,service关键字和后面的参数会保存在args[]数组中。然后通过对args[0]提取关键字,发现args[0]=”service”,于是开始执行parse_new_section函数。此时这个函数必然会进入 case K_service:分支执行:

    case K_service:        state->context = parse_service(state, nargs, args);        if (state->context) {            state->parse_line = parse_line_service;            return;        }        break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里做了两件事情非常重要,一件是调用parse_service解析service这个section。另一件事情是给state->parse_line赋值为parse_line_service。也就是service 关键字所在的行后面的那些options行都是使用parse_line_service函数来解析的。我们从parse_service看起:

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

可以看到和import做的事情差不多。import解析的最后,会创建一个import结构体,并把它添加到import_list双向链表中。service解析从这里看,也是构建一个service结构体,然后把service结构体添加到service_list链表中。
我们看下service结构体:

struct service {    void NotifyStateChange(const char* new_state);        /* list of all services */    struct listnode slist;    char *name;    const char *classname;    unsigned flags;    pid_t pid;    time_t time_started;    /* time of last start */    time_t time_crashed;    /* first crash within inspection window */    int nr_crashed;         /* number of times crashed within window */    uid_t uid;    gid_t gid;    gid_t supp_gids[NR_SVC_SUPP_GIDS];    size_t nr_supp_gids;    const char* seclabel;    struct socketinfo *sockets;    struct svcenvinfo *envvars;    struct action onrestart;  /* Actions to execute on restart. */    std::vector<std::string>* writepid_files_;    /* keycodes for triggering this service via /dev/keychord */    int *keycodes;    int nkeycodes;    int keychord_id;    IoSchedClass ioprio_class;    int ioprio_pri;    int nargs;    /* "MUST BE AT THE END OF THE STRUCT" */    char *args[1];}; /*     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

socketinfo 用来保存socket option的相关信息。
classname 给service定义一个类名,如果多个service使用相同的类型,可以方便进行批量操作。
nargs 保存参数的个数。
很多字段不理解,没关系,我们看看parse_service函数给service做了那些初始化:
1. svc->name = strdup(args[1]);名字就是service 关键字后面的第一个参数
2. svc->classname = “default”; 类别名是default
3. memcpy(svc->args, args + 2, sizeof(char*) * nargs); svc->args[nargs] = 0;
把所有参数保存在args数组中,并把最有一个成员只为0。
4. svc->nargs = nargs; nargs保存参数个数
5. trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
list_init(&svc->onrestart.triggers);
cur_trigger->name = “onrestart”;
list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
构建一个触发器,并把它添加到service中的onrestart.triger列表中。
因此,我们可以知道么一个service都会有一个onrestart的action,这个action有一个触发器。这个action用来重启service。
解析完service后,就会解析service后面的option了,这个时候会调用parse_line_service。

static void parse_line_service(struct parse_state *state, int nargs, char **args){    struct service *svc = (service*) state->context;    struct command *cmd;    int i, kw, kw_nargs;    if (nargs == 0) {        return;    }    svc->ioprio_class = IoSchedClass_NONE;    kw = lookup_keyword(args[0]);    switch (kw) {    case K_class:        if (nargs != 2) {            parse_error(state, "class option requires a classname\n");        } else {            svc->classname = args[1];        }        break;    case K_console:        svc->flags |= SVC_CONSOLE;        break;    case K_disabled:        svc->flags |= SVC_DISABLED;        svc->flags |= SVC_RC_DISABLED;        break;    case K_ioprio:        if (nargs != 3) {            parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");        } else {            svc->ioprio_pri = strtoul(args[2], 0, 8);            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {                parse_error(state, "priority value must be range 0 - 7\n");                break;            }            if (!strcmp(args[1], "rt")) {                svc->ioprio_class = IoSchedClass_RT;            } else if (!strcmp(args[1], "be")) {                svc->ioprio_class = IoSchedClass_BE;            } else if (!strcmp(args[1], "idle")) {                svc->ioprio_class = IoSchedClass_IDLE;            } else {                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");            }        }        break;    case K_group:        if (nargs < 2) {            parse_error(state, "group option requires a group id\n");        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {            parse_error(state, "group option accepts at most %d supp. groups\n",                        NR_SVC_SUPP_GIDS);        } else {            int n;            svc->gid = decode_uid(args[1]);            for (n = 2; n < nargs; n++) {                svc->supp_gids[n-2] = decode_uid(args[n]);            }            svc->nr_supp_gids = n - 2;        }        break;    case K_keycodes:        if (nargs < 2) {            parse_error(state, "keycodes option requires atleast one keycode\n");        } else {            svc->keycodes = (int*) malloc((nargs - 1) * sizeof(svc->keycodes[0]));            if (!svc->keycodes) {                parse_error(state, "could not allocate keycodes\n");            } else {                svc->nkeycodes = nargs - 1;                for (i = 1; i < nargs; i++) {                    svc->keycodes[i - 1] = atoi(args[i]);                }            }        }        break;    case K_oneshot:        svc->flags |= SVC_ONESHOT;        break;    case K_onrestart:        nargs--;        args++;        kw = lookup_keyword(args[0]);        if (!kw_is(kw, COMMAND)) {            parse_error(state, "invalid command '%s'\n", args[0]);            break;        }        kw_nargs = kw_nargs(kw);        if (nargs < kw_nargs) {            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,                kw_nargs > 2 ? "arguments" : "argument");            break;        }        cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);        cmd->func = kw_func(kw);        cmd->nargs = nargs;        memcpy(cmd->args, args, sizeof(char*) * nargs);        list_add_tail(&svc->onrestart.commands, &cmd->clist);        break;    case K_critical:        svc->flags |= SVC_CRITICAL;        break;    case K_setenv: { /* name value */        if (nargs < 3) {            parse_error(state, "setenv option requires name and value arguments\n");            break;        }        svcenvinfo* ei = (svcenvinfo*) calloc(1, sizeof(*ei));        if (!ei) {            parse_error(state, "out of memory\n");            break;        }        ei->name = args[1];        ei->value = args[2];        ei->next = svc->envvars;        svc->envvars = ei;        break;    }    case K_socket: {/* name type perm [ uid gid context ] */        if (nargs < 4) {            parse_error(state, "socket option requires name, type, perm arguments\n");            break;        }        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")                && strcmp(args[2],"seqpacket")) {            parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");            break;        }        socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));        if (!si) {            parse_error(state, "out of memory\n");            break;        }        si->name = args[1];        si->type = args[2];        si->perm = strtoul(args[3], 0, 8);        if (nargs > 4)            si->uid = decode_uid(args[4]);        if (nargs > 5)            si->gid = decode_uid(args[5]);        if (nargs > 6)            si->socketcon = args[6];        si->next = svc->sockets;        svc->sockets = si;        break;    }    case K_user:        if (nargs != 2) {            parse_error(state, "user option requires a user id\n");        } else {            svc->uid = decode_uid(args[1]);        }        break;    case K_seclabel:        if (nargs != 2) {            parse_error(state, "seclabel option requires a label string\n");        } else {            svc->seclabel = args[1];        }        break;    case K_writepid:        if (nargs < 2) {            parse_error(state, "writepid option requires at least one filename\n");            break;        }        svc->writepid_files_ = new std::vector<std::string>;        for (int i = 1; i < nargs; ++i) {            svc->writepid_files_->push_back(args[i]);        }        break;    default:        parse_error(state, "invalid option '%s'\n", args[0]);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181

这个函数中定义了所有的option,每一个option处理方法都不相同,大家遇到感兴趣的option可以自行分析。
service解析完成后,是时候看看service的启动了。

service的启动

service解析完成以后,有了一个service_list的链表,可以service是在是么地方启动的呢?当然是在action中了,action中有个命名叫start,它对应的处理函数是do_start,着我们在前面已经说过了,do_start函数如下:

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

这个函数非常见到,找到服务,启动它。
查找service的过程:

struct service *service_find_by_name(const char *name){    struct listnode *node;    struct service *svc;    list_for_each(node, &service_list) {        svc = node_to_item(node, struct service, slist);        if (!strcmp(svc->name, name)) {            return svc;        }    }    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

遍历service_list,对比名字,相同就返回。
启动service过程:

void service_start(struct service *svc, const char *dynamic_args){    // Starting a service removes it from the disabled or reset state and    // immediately takes it out of the restarting state if it was in there.    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));    svc->time_started = 0;    // Running processes require no additional work --- if they're in the    // process of exiting, we've ensured that they will immediately restart    // on exit, unless they are ONESHOT.    if (svc->flags & SVC_RUNNING) {        return;    }    bool needs_console = (svc->flags & SVC_CONSOLE);    if (needs_console && !have_console) {        ERROR("service '%s' requires console\n", svc->name);        svc->flags |= SVC_DISABLED;        return;    }    struct stat s;    if (stat(svc->args[0], &s) != 0) {        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);        svc->flags |= SVC_DISABLED;        return;    }    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",               svc->args[0]);        svc->flags |= SVC_DISABLED;        return;    }    char* scon = NULL;    if (is_selinux_enabled() > 0) {        if (svc->seclabel) {            scon = strdup(svc->seclabel);            if (!scon) {                ERROR("Out of memory while starting '%s'\n", svc->name);                return;            }        } else {            char *mycon = NULL, *fcon = NULL;            INFO("computing context for service '%s'\n", svc->args[0]);            int rc = getcon(&mycon);            if (rc < 0) {                ERROR("could not get context while starting '%s'\n", svc->name);                return;            }            rc = getfilecon(svc->args[0], &fcon);            if (rc < 0) {                ERROR("could not get context while starting '%s'\n", svc->name);                freecon(mycon);                return;            }            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);            if (rc == 0 && !strcmp(scon, mycon)) {                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);            }            freecon(mycon);            freecon(fcon);            if (rc < 0) {                ERROR("could not get context while starting '%s'\n", svc->name);                return;            }        }    }    NOTICE("Starting service '%s'...\n", svc->name);    pid_t pid = fork();    if (pid == 0) {        struct socketinfo *si;        struct svcenvinfo *ei;        char tmp[32];        int fd, sz;        umask(077);        if (properties_initialized()) {            get_property_workspace(&fd, &sz);            snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);        }        for (ei = svc->envvars; ei; ei = ei->next)            add_environment(ei->name, ei->value);        for (si = svc->sockets; si; si = si->next) {            int socket_type = (                    !strcmp(si->type, "stream") ? SOCK_STREAM :                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));            int s = create_socket(si->name, socket_type,                                  si->perm, si->uid, si->gid, si->socketcon ?: scon);            if (s >= 0) {                publish_socket(si->name, s);            }        }        freecon(scon);        scon = NULL;        if (svc->writepid_files_) {            std::string pid_str = android::base::StringPrintf("%d", pid);            for (auto& file : *svc->writepid_files_) {                if (!android::base::WriteStringToFile(pid_str, file)) {                    ERROR("couldn't write %s to %s: %s\n",                          pid_str.c_str(), file.c_str(), strerror(errno));                }            }        }        if (svc->ioprio_class != IoSchedClass_NONE) {            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));            }        }        if (needs_console) {            setsid();            open_console();        } else {            zap_stdio();        }        if (false) {            for (size_t n = 0; svc->args[n]; n++) {                INFO("args[%zu] = '%s'\n", n, svc->args[n]);            }            for (size_t n = 0; ENV[n]; n++) {                INFO("env[%zu] = '%s'\n", n, ENV[n]);            }        }        setpgid(0, getpid());        // As requested, set our gid, supplemental gids, and uid.        if (svc->gid) {            if (setgid(svc->gid) != 0) {                ERROR("setgid failed: %s\n", strerror(errno));                _exit(127);            }        }        if (svc->nr_supp_gids) {            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {                ERROR("setgroups failed: %s\n", strerror(errno));                _exit(127);            }        }        if (svc->uid) {            if (setuid(svc->uid) != 0) {                ERROR("setuid failed: %s\n", strerror(errno));                _exit(127);            }        }        if (svc->seclabel) {            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));                _exit(127);            }        }        if (!dynamic_args) {            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));            }        } else {            char *arg_ptrs[INIT_PARSER_MAXARGS+1];            int arg_idx = svc->nargs;            char *tmp = strdup(dynamic_args);            char *next = tmp;            char *bword;            /* Copy the static arguments */            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));            while((bword = strsep(&next, " "))) {                arg_ptrs[arg_idx++] = bword;                if (arg_idx == INIT_PARSER_MAXARGS)                    break;            }            arg_ptrs[arg_idx] = NULL;            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);        }        _exit(127);    }    freecon(scon);    if (pid < 0) {        ERROR("failed to start '%s'\n", svc->name);        svc->pid = 0;        return;    }    svc->time_started = gettime();    svc->pid = pid;    svc->flags |= SVC_RUNNING;    if ((svc->flags & SVC_EXEC) != 0) {        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,             svc->seclabel ? : "default");        waiting_for_exec = true;    }    svc->NotifyStateChange("running");}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214

这个函数虽然长,但总结起来无非就做了这些事情:
1.解析参数
2.fork一个进程。
3.初始化子进程,主要是根据service结构体中的信息创建一个写东西。比如,根据sockets创建socket等。
4.执行execve,也就是加载可执行文件了。

也就是说,当init.rc等rc配置脚本解析完成后,开始执行action中的命令,并通过start命令来启动service。

action的解析与命令的执行

action的解析

对比service的解析来看,action的解析应该是调用parse_action函数:

static void *parse_action(struct parse_state *state, int nargs, char **args){    struct trigger *cur_trigger;    int i;    if (nargs < 2) {        parse_error(state, "actions must have a trigger\n");        return 0;    }    action* act = (action*) calloc(1, sizeof(*act));    list_init(&act->triggers);    for (i = 1; i < nargs; i++) {        if (!(i % 2)) {            if (strcmp(args[i], "&&")) {                struct listnode *node;                struct listnode *node2;                parse_error(state, "& is the only symbol allowed to concatenate actions\n");                list_for_each_safe(node, node2, &act->triggers) {                    struct trigger *trigger = node_to_item(node, struct trigger, nlist);                    free(trigger);                }                free(act);                return 0;            } else                continue;        }        cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));        cur_trigger->name = args[i];        list_add_tail(&act->triggers, &cur_trigger->nlist);    }    list_init(&act->commands);    list_init(&act->qlist);    list_add_tail(&action_list, &act->alist);        /* XXX add to hash */    return act;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

action中命令的执行

可以看到所有的section解析都是类似的,构建一个结构体并添加到对应的链表中。
这里就不继续展开分析了。我们回到init.cpp的main函数中,看看action_list中存放的action是如何被执行的。
在解析init.rc结束后,会有如下函数:

 action_for_each_trigger("early-init", action_add_queue_tail); action_for_each_trigger("init", action_add_queue_tail); action_for_each_trigger("charger", action_add_queue_tail); action_for_each_trigger("late-init", action_add_queue_tail);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

action_for_each_trigger函数如下:

void action_for_each_trigger(const char *trigger,                             void (*func)(struct action *act)){    struct listnode *node, *node2;    struct action *act;    struct trigger *cur_trigger;    list_for_each(node, &action_list) {        act = node_to_item(node, struct action, alist);        list_for_each(node2, &act->triggers) {            cur_trigger = node_to_item(node2, struct trigger, nlist);            if (!strcmp(cur_trigger->name, trigger)) {                func(act);            }        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

也就是说,这个函数的作用是遍历action_list链表,找到对应名字的触发器,然后盗用传入的func函数,也就是action_add_queue_tail函数,这个函数如下:

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

再把action添加到action_queuw中。这里是不是可以理解为给action排队呢?按照action的名字(early-init,init…)把action排好顺序。这个时候还是没有执行action中的命令。
继续往下看,看到execute_one_command函数,从名字来看是执行一个命令,我们看看是怎么执行的,execute_one_command函数如下:

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

这个函数就是从aciton_queue中取出头部的action,然后执行command中的函数。那这个函数是什么呢?我们在文章一开始就说过了,每一个command都对应一个do_xxxx的函数来处理该命令。是不是这样呢?
我们需要从command的解析说起。和解析service的option一样。解析command使用的是parse_line_action函数。这里不明白的可以返回去看看service的解析过程。
parse_line_action定义在system/core/init/init_parser.cpp中,该函数如下:

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

关键的代码只有一行cmd->func = kw_func(kw),kw_func函数定义在相同文件下,是一个宏:

#define kw_func(kw) (keyword_info[kw].func)
  • 1
  • 1

这里keyword_info数组又出现了吧,这个数组也定义在system/core/init/init_parser.cpp中:

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

可以看到数组的初始化使用的是#include “keywords.h”。这中写法我还是第一次见到。
keywords.h:

#ifndef KEYWORDint do_bootchart_init(int nargs, char **args);int do_class_start(int nargs, char **args);int do_class_stop(int nargs, char **args);int do_class_reset(int nargs, char **args);int do_domainname(int nargs, char **args);int do_enable(int nargs, char **args);int do_exec(int nargs, char **args);int do_export(int nargs, char **args);int do_hostname(int nargs, char **args);int do_ifup(int nargs, char **args);int do_insmod(int nargs, char **args);int do_installkey(int nargs, char **args);int do_mkdir(int nargs, char **args);int do_mount_all(int nargs, char **args);int do_mount(int nargs, char **args);int do_powerctl(int nargs, char **args);int do_restart(int nargs, char **args);int do_restorecon(int nargs, char **args);int do_restorecon_recursive(int nargs, char **args);int do_rm(int nargs, char **args);int do_rmdir(int nargs, char **args);int do_setprop(int nargs, char **args);int do_setrlimit(int nargs, char **args);int do_start(int nargs, char **args);int do_stop(int nargs, char **args);int do_swapon_all(int nargs, char **args);int do_trigger(int nargs, char **args);int do_symlink(int nargs, char **args);int do_sysclktz(int nargs, char **args);int do_write(int nargs, char **args);int do_copy(int nargs, char **args);int do_chown(int nargs, char **args);int do_chmod(int nargs, char **args);int do_loglevel(int nargs, char **args);int do_load_persist_props(int nargs, char **args);int do_load_all_props(int nargs, char **args);int do_verity_load_state(int nargs, char **args);int do_verity_update_state(int nargs, char **args);int do_wait(int nargs, char **args);#define __MAKE_KEYWORD_ENUM__#define KEYWORD(symbol, flags, nargs, func) K_##symbol,enum {    K_UNKNOWN,#endif    KEYWORD(bootchart_init,        COMMAND, 0, do_bootchart_init)    KEYWORD(chmod,       COMMAND, 2, do_chmod)    KEYWORD(chown,       COMMAND, 2, do_chown)    KEYWORD(class,       OPTION,  0, 0)    KEYWORD(class_reset, COMMAND, 1, do_class_reset)    KEYWORD(class_start, COMMAND, 1, do_class_start)    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)    KEYWORD(console,     OPTION,  0, 0)    KEYWORD(copy,        COMMAND, 2, do_copy)    KEYWORD(critical,    OPTION,  0, 0)    KEYWORD(disabled,    OPTION,  0, 0)    KEYWORD(domainname,  COMMAND, 1, do_domainname)    KEYWORD(enable,      COMMAND, 1, do_enable)    KEYWORD(exec,        COMMAND, 1, do_exec)    KEYWORD(export,      COMMAND, 2, do_export)    KEYWORD(group,       OPTION,  0, 0)    KEYWORD(hostname,    COMMAND, 1, do_hostname)    KEYWORD(ifup,        COMMAND, 1, do_ifup)    KEYWORD(import,      SECTION, 1, 0)    KEYWORD(insmod,      COMMAND, 1, do_insmod)    KEYWORD(installkey,  COMMAND, 1, do_installkey)    KEYWORD(ioprio,      OPTION,  0, 0)    KEYWORD(keycodes,    OPTION,  0, 0)    KEYWORD(load_all_props,        COMMAND, 0, do_load_all_props)    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)    KEYWORD(mount_all,   COMMAND, 1, do_mount_all)    KEYWORD(mount,       COMMAND, 3, do_mount)    KEYWORD(oneshot,     OPTION,  0, 0)    KEYWORD(onrestart,   OPTION,  0, 0)    KEYWORD(on,          SECTION, 0, 0)    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)    KEYWORD(restart,     COMMAND, 1, do_restart)    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)    KEYWORD(restorecon_recursive,  COMMAND, 1, do_restorecon_recursive)    KEYWORD(rm,          COMMAND, 1, do_rm)    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)    KEYWORD(seclabel,    OPTION,  0, 0)    KEYWORD(service,     SECTION, 0, 0)    KEYWORD(setenv,      OPTION,  2, 0)    KEYWORD(setprop,     COMMAND, 2, do_setprop)    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)    KEYWORD(socket,      OPTION,  0, 0)    KEYWORD(start,       COMMAND, 1, do_start)    KEYWORD(stop,        COMMAND, 1, do_stop)    KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)    KEYWORD(symlink,     COMMAND, 1, do_symlink)    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)    KEYWORD(trigger,     COMMAND, 1, do_trigger)    KEYWORD(user,        OPTION,  0, 0)    KEYWORD(verity_load_state,      COMMAND, 0, do_verity_load_state)    KEYWORD(verity_update_state,    COMMAND, 0, do_verity_update_state)    KEYWORD(wait,        COMMAND, 1, do_wait)    KEYWORD(write,       COMMAND, 2, do_write)    KEYWORD(writepid,    OPTION,  0, 0)#ifdef __MAKE_KEYWORD_ENUM__    KEYWORD_COUNT,};#undef __MAKE_KEYWORD_ENUM__#undef KEYWORD#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

这其中定义了所有的init.rc中需要的关键字。从中可以知道哪些是命令,哪些是option,哪些是sercion。此外,也验证我们说的命令(command)都对应这一个do_xxxx的函数,执行这些命令其实就是执行这些函数。





0 0
原创粉丝点击