源码分析之init进程

init为安卓系统开启后,由kernel引导的第一个进程,其通过解析代码中不同的init.rc脚本,不断的将系统服务启动起来,具体的脚本文件已在之前博文中进行了介绍。本章节主要介绍init进程的启动流程。

init流程分析

  • 主要任务

    1. 第一个应用程序
    2. 创建目录,挂在分区
    3. 解析应用脚本
    4. 启动服务
    5. 守护服务
  • 主要过程

    1. 创建目录
    2. klog_init() 将log重定向到/proc/kmsg中
    3. property_init() 初始化属性服务
    4. signal_handler_init() 初始化子进程退出的信号处理过程
    5. property_load_boot_defaults() 初始化环境变量
    6. init_parse_config_file(“/init.rc”) 解析init.rc
    7. action_for_each_trigger(…) 执行rc脚本文件中对应的触发器
    8. queue_builtin_action(…) 构建自定义事件,加入队列
    9. execute_one_command() 死循环,执行脚本命令(启动进程)
    10. restart_processes() 重启服务进程(守护进程)
    11. epoll_wait() 监听管道,等待事件
  • 在分析init.cpp的具体流程,先来回顾下init.rc脚本的主要组成部分

安卓源码分析之init.rc脚本

rc脚本文件可以用以下几张图片概括

  • 文件结构
init.rc文件路径在/system/core/rootdir/init.rc下存在,根据不同的平台又有自己的rc脚本,通过import导入
  • 导包内容
其中 ${ ro.hardware } 的值可以通过 getprop ro.harware 命令获取,其对应于特定平台的rc文件,例如 getprop ro.hardware = mooney , 那么导包对应的rc脚本文件为init.mooney.rc
  • 触发器
脚本中声明多个 on ... 触发器,可以通过代码action_for_each_trigger(...)触发实现
  • service服务
service服务脚本声明:指定脚本文件名称,路径,启动参数,类属以及其他属性值

init进程具体流程分析

init 主要的执行流程如下图所示

初始化 kernel log ,位于设备节点/dev/kmsg

–> klog.c

void klog_init(void) {
    if (klog_fd >= 0) return; /* Already initialized */

    // 打开指定设备
    klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
    if (klog_fd >= 0) {
        return;
    }

    static const char* name = "/dev/__kmsg__";
    // 创建节点
    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
        // 尝试打开
        klog_fd = open(name, O_WRONLY | O_CLOEXEC);
        unlink(name);
    }
}

初始化子进程退出的信号处理过程signal_handler_init()

–> signal_handler.cpp

void signal_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    // 创建socket pair套接字
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        ERROR("socketpair failed: %s\n", strerror(errno));
        exit(1);
    }

    // 赋值
    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    // 当捕获SIGCHLD信号,则写入signal_write_fd
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);

    reap_any_outstanding_children();

    // 注册监听处理
    register_epoll_handler(signal_read_fd, handle_signal);
}


static void reap_any_outstanding_children() {
    while (wait_for_one_process()) {
    }
}


static bool wait_for_one_process() {
    int status;
    //等待任意子进程,如果子进程没有退出则返回0,否则返回盖子进程的pid
    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
    if (pid == 0) {
        return false;
    } else if (pid == -1) {
        ERROR("waitpid failed: %s\n", strerror(errno));
        return false;
    }

    //根据pid查找相应的service
    service* svc = service_find_by_pid(pid);

    std::string name;
    if (svc) {
        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
    } else {
        name = android::base::StringPrintf("Untracked pid %d", pid);
    }

    NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());

    if (!svc) {
        return true;
    }

    // TODO: all the code from here down should be a member function on service.
    // 当flag为RESTART,且不是ONESHOT时,先kill进程组内的所有的子进程或子线程
    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
        NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
        kill(-pid, SIGKILL);
    }

    // Remove any sockets we may have created.
    // 移除当前服务中所有创建过的socket
    for (socketinfo* si = svc->sockets; si; si = si->next) {
        char tmp[128];
        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
        unlink(tmp);
    }

    // 当flag为EXEC时,释放相应的服务
    if (svc->flags & SVC_EXEC) {
        INFO("SVC_EXEC pid %d finished...\n", svc->pid);
        waiting_for_exec = false;
        list_remove(&svc->slist);
        free(svc->name);
        free(svc);
        return true;
    }

    svc->pid = 0;
    svc->flags &= (~SVC_RUNNING);

    // Oneshot processes go into the disabled state on exit,
    // except when manually restarted.
    // 对于ONESHOT服务,使其进入disabled状态
    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
        svc->flags |= SVC_DISABLED;
    }

    // Disabled and reset processes do not get restarted automatically.
    // 禁用和重置的服务,都不在自动重启
    if (svc->flags & (SVC_DISABLED | SVC_RESET))  {
        svc->NotifyStateChange("stopped");
        return true;
    }

    time_t now = gettime();
    // 服务在4分钟内重启次数超过4次,则重启进入recovery模式
    if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
                ERROR("critical process '%s' exited %d times in %d minutes; "
                      "rebooting into recovery mode\n", svc->name,
                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
                return true;
            }
        } else {
            svc->time_crashed = now;
            svc->nr_crashed = 1;
        }
    }

    svc->flags &= (~SVC_RESTART);
    svc->flags |= SVC_RESTARTING;

    // Execute all onrestart commands for this service.
    // 执行当前service中的所有onstart命令
    struct listnode* node;
    list_for_each(node, &svc->onrestart.commands) {
        command* cmd = node_to_item(node, struct command, clist);
        cmd->func(cmd->nargs, cmd->args);
    }
    svc->NotifyStateChange("restarting");
    return true;
}

加载 *.prop 属性文件

–> property_service.cpp

void property_load_boot_defaults() {
    //加载指定文件路径下的 *.prop 文件
    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}


static void load_properties_from_file(const char* filename, const char* filter) {
    Timer t;
    std::string data;
    if (read_file(filename, &data)) {
        data.push_back('\n');
        load_properties(&data[0], filter);
    }
    NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
}


//解析文件到指定数据data结构中
static void load_properties(char *data, const char *filter)
{
    char *key, *value, *eol, *sol, *tmp, *fn;
    size_t flen = 0;

    if (filter) {
        flen = strlen(filter);
    }

    sol = data;
    while ((eol = strchr(sol, '\n'))) {
        key = sol;
        *eol++ = 0;
        sol = eol;

        while (isspace(*key)) key++;
        if (*key == '#') continue;

        tmp = eol - 2;
        while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;

        // import节点
        if (!strncmp(key, "import ", 7) && flen == 0) {
            fn = key + 7;
            while (isspace(*fn)) fn++;

            key = strchr(fn, ' ');
            if (key) {
                *key++ = 0;
                while (isspace(*key)) key++;
            }

            // 导入import节点文件,再次解析
            load_properties_from_file(fn, key);

        } else {
            value = strchr(key, '=');
            if (!value) continue;
            *value++ = 0;

            tmp = value - 2;
            while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;

            while (isspace(*value)) value++;

            if (flen > 0) {
                if (filter[flen - 1] == '*') {
                    if (strncmp(key, filter, flen - 1)) continue;
                } else {
                    if (strcmp(key, filter)) continue;
                }
            }

            // 设置属性
            property_set(key, value);
        }
    }
}

启动属性服务器(通过sockert通信)

–> property_service.cpp

// 开启属性服务
void start_property_service() {
    // 创建seocker套接字
    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                    0666, 0, 0, NULL);
    if (property_set_fd == -1) {
        ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
        exit(1);
    }

    // 加入监听
    listen(property_set_fd, 8);

    // 注册监听
    register_epoll_handler(property_set_fd, handle_property_set_fd);
}

用户可以通过串口开启、结束指定服务,通过命令setprop更改系统属性值,三方app也可以通过指定的api更改系统属性(权限限制),其到底层都是通过socket机制,通过init中的handle_property_***()方法实现。其流程模型如下:

解析 *.rc 脚本文件

–> init_parser.cpp

// 解析指定路径下文件
int init_parse_config_file(const char* path) {
    INFO("Parsing %s...\n", path);
    Timer t;
    std::string data;
    // 读取文件信息到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;
}


// 解析脚本文件信息
static void parse_config(const char *fn, const std::string& data)
{
    struct listnode import_list;
    struct listnode *node;
    char *args[INIT_PARSER_MAXARGS];

    int nargs = 0;

    parse_state state;
    state.filename = fn;
    state.line = 0;
    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;

    for (;;) {
        switch (next_token(&state)) {
        case T_EOF:    // 导入行
            state.parse_line(&state, 0, 0);
            goto parser_done;
        case T_NEWLINE:    // 命令行
            state.line++;
            if (nargs) {
                int kw = lookup_keyword(args[0]);
                if (kw_is(kw, SECTION)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }

parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         int ret;

         // 解析导入文件
         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}


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;
}


// 根据命令匹配相应的操作
// 通过宏定义匹配相应的操作指令
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;
}

根据脚本文件不同的标签分类解析

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) {
    // service 标签
    case K_service:
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
    // on 标签
    case K_on:
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
    // import 标签
    case K_import:
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line = parse_line_no_op;
}

通过不同标签分类解析并加入对应的链表

    // 解析脚本中的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;
    }


        // 解析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]);
            }
        }



        // 解析脚本中的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;
        }


    // 解析脚本中action的具体信息,加入action链表中
    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);
    }


    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);
    }

{% asset_img jiaobenfenxijieguo.png jiaobenfenxijieguo.png %}

执行脚本中指定触发器的操作

–> init.cpp

// 找到解析列表的action并运行
void action_for_each_trigger(const char *trigger,
                             void (*func)(struct action *act))
{
    struct listnode *node, *node2;
    struct action *act;
    struct trigger *cur_trigger;

    // 找到脚本文件中指定的node节点
    list_for_each(node, &action_list) {
        // 获取指定节点的action
        act = node_to_item(node, struct action, alist);
        // 执行节点下fun()
        list_for_each(node2, &act->triggers) {
            cur_trigger = node_to_item(node2, struct trigger, nlist);
            if (!strcmp(cur_trigger->name, trigger)) {
                func(act);
            }
        }
    }
}

通过指定的触发器,遍历解析的rc脚本文件相应command,执行具体的操作,流程图如下:

构建事件,并加入队列中

–> init.cpp

// 构建事件,并加入队列中
void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
{
    action* act = (action*) calloc(1, sizeof(*act));
    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
    cur_trigger->name = name;
    list_init(&act->triggers);
    list_add_tail(&act->triggers, &cur_trigger->nlist);
    list_init(&act->commands);
    list_init(&act->qlist);

    command* cmd = (command*) calloc(1, sizeof(*cmd));
    cmd->func = func;
    cmd->args[0] = const_cast<char*>(name);
    cmd->nargs = 1;
    list_add_tail(&act->commands, &cmd->clist);

    list_add_tail(&action_list, &act->alist);
    action_add_queue_tail(act);
}

根据构建自定事件,并加入对列中,通指定触发器来触发操作,增加灵活性,如下图所示流程:

开启服务(死循环)

–> init.cpp

// 执行命令
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)) {
        //    获取链表的第一个action
        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);
        // 获取第一条command命令
        cur_command = get_first_command(cur_action);
    } else {
        // 下一跳command命令
        cur_command = get_next_command(cur_action, cur_command);
    }

    if (!cur_command) {
        return;
    }

    // 调用相应command的fun() 方法
    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());
    }
}


// 返回头部action并移除队列
struct action *action_remove_queue_head(void)
{
    if (list_empty(&action_queue)) {
        return 0;
    } else {
        struct listnode *node = list_head(&action_queue);
        struct action *act = node_to_item(node, struct action, qlist);
        list_remove(node);
        list_init(node);
        return act;
    }
}

如下所示,是init进程执行开启服务的具体流程:

  1. 首先从action_queue中查找到cmd
  2. 根据cmd对应的宏定义KEYWORD
  3. 执行对应的func方法
  4. 例如service,遍历解析的services列表,找到指定的service
  5. 启动指定的service服务

守护进程,重启服务

–> init.cpp

// 重启服务
static void restart_processes()
{
    process_needs_restart = 0;
    // 根据flag=SVC_RESTARTING,有选择的重启
    service_for_each_flags(SVC_RESTARTING,
                           restart_service_if_needed);
}


// 根据服务名称找到解析列表中对应的服务并启动起来
void service_for_each_class(const char *classname,
                            void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    // 找到列表中对应服务的节点
    list_for_each(node, &service_list) {
        svc = node_to_item(node, struct service, slist);
        if (!strcmp(svc->classname, classname)) {
            // 启动服务
            func(svc);
        }
    }
}
  1. 内核通过fork产生父子两个进程,之间通过socket产生通信
  2. init进程死循环等待socket内的消息
  3. 如果出现服务死掉,根据进程pid,找到脚本解析文件中对应的service
  4. 启动找到的service服务(是否起启动取决service中具体的约束内容)

具体的消息传递流程如下:

总结

  1. init 脚本中将服务统一归纳起来,由 init.cpp 统一解析,开启服务并守护,实现了两者之间的解耦
  2. 通过import导入相关脚本文件,初始化属性服务,开启socket通信处理,解析脚本文件,开启守护进程服务,死循环等待socket通信
  3. 一旦有服务异常死掉,便会通过socket通信机制,依据pid找到之前解析的脚本内容,重启服务
  4. 可以适当在脚本中添加代码实现自定义开机启动服务
坚持原创技术分享,您的支持将鼓励我继续创作!