当前位置: 代码迷 >> Android >> Android init历程——解析配置文件
  详细解决方案

Android init历程——解析配置文件

热度:80   发布时间:2016-04-28 01:22:31.0
Android init进程——解析配置文件

目录

  • 目录
  • init解析配置文件
    • 关键字定义
    • kw_is
    • 解析
      • K_import
      • K_on
      • command执行
      • K_service
  • service
    • service结构体
    • parse_service
    • parse_line_service
    • init控制service


init解析配置文件

在解析service服务是如何启动之前,让我们先来学习一下init进程是如何解析init.rc等配置文件的。

init进程解析配置文件的代码如下(/system/core/init/init.c&&/system/core/init/init_parser.c):

init_parse_config_file("/init.rc");void *read_file(const char *fn, unsigned *_sz){    char *data;    int sz;    int fd;    struct stat sb;    data = 0;    fd = open(fn, O_RDONLY);    if (fd < 0) return 0;    // 获取init.rc文件字符总数    sz = lseek(fd, 0, SEEK_END);    if (sz < 0) goto oops;    if (lseek(fd, 0, SEEK_SET) != 0) goto oops;    // 多余两个字符存储'\n'和'\0'    data = (char*)malloc(sz + 2);    if (data == 0) goto oops;    // 读取init.rc文件内容到data数组中    if (read(fd, data, sz) != sz) goto oops;    close(fd);    data[sz] = '\n';    data[sz + 1] = 0;    if(_sz) *_sz = sz;    return data;oops:    close(fd);    if (data != 0) free(data);    return 0;}int init_parse_config_file(const char *fn){    char *data;    data = read_file(fn, 0);    if (!data) return -1;    parse_config(fn, data);    DUMP();    return 0;}

读完init.rc文件内容到data数组中后,将调用parse_config函数进行解析,具体函数源码如下:

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;};#define T_EOF 0#define T_TEXT 1#define T_NEWLINE 2int 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;        }    }text:    state->text = s = x;}static void parse_config(const char *fn, char *s){    struct parse_state state;    struct listnode import_list;    struct listnode *node;    char *args[INIT_PARSER_MAXARGS];    int nargs;    nargs = 0;    state.filename = fn;    state.line = 0;    state.ptr = s;    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 (ks_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:    // 解析import关键字导入的rc文件    list_for_each(node, &import_list) {        struct import *import = node_to_item(node, struct import, list);        int ret;        INFO("importing '%s'", import->filename);        ret = init_parse_config_file(import->filename);        if (ret) {            ERROR("could not import file '%s' from '%s'\n", import->filename, fn);        }    }}

上面就是parse_config函数的实现。函数通过调用next_token函数来解析data数组内容,当有新的一行有效数据时,则把有效数据存入到args字符指针数组中,然后在T_NEWLINE分支中进行真正的解析,这也是解析过程中最复杂的地方。让我们来深入的学习一下T_NEWLINE分支的解析过程。


关键字定义

我们先来看一下loopup_keyword函数的具体实现,源码位于/system/core/init/init_parser.c文件中,源码内容如下:

int lookup_keyword(const char *s){    switch(*s ++) {    case 'c':    if (!strcmp(s, "opy")) return K_copy;    if (!strcmp(s, "apability")) return K_capability;    if (!strcmp(s, "hdir")) return K_chdir;    if (!strcmp(s, "hroot")) return K_chroot;    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, "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;        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;        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, "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, "etcon")) return K_setcon;        if (!strcmp(s, "etenforce")) return K_setenforce;        if (!strcmp(s, "etenv")) return K_setenv;        if (!strcmp(s, "etkey")) return K_setkey;        if (!strcmp(s, "etprop")) return K_setprop;        if (!strcmp(s, "etrlimit")) return K_setrlimit;        if (!strcmp(s, "etsebool")) return K_setsebool;        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 'w':        if (!strcmp(s, "rite")) return K_write;        if (!strcmp(s, "ait")) return K_wait;        break;    }    return K_UNKNOWN;}

lookup_keyword函数源码是非常简单的,罗列了一堆strcmp语句。而且源码在匹配case ‘p’的时候有一个明显的问题,大家感兴趣的可以自行查看Android源码。

接下来,判断关键字类型是不是SECTION,我们继续来看一下kw_is的源码。


kw_is

kw_is函数的源码实现如下:

#define SECTION 0x01#define kw_is(kw, type) (keyword_info[kw].flags & (type))

从上述代码中我们还不能搞清楚道理什么类型算是SECTION类型,这里直接告诉大家:只有当关键字为on、service和import的时候,关键字才被认为是SECTION


解析

虽然关键字的类型很多,但是大部分关键字对应的解析函数都是parse_line_no_op。而parse_line_no_op的实现却是空的,源码如下:

void parse_line_no_op(struct parse_state *state, int nargs, char **args){}

为什么会这样呢?

是因为,仔细分析最初的init_parse_config_file函数要解析的init.rc文件,虽然init.rc的内容很多,但是其中只有三种有效模式:

  1. 第一种trigger触发
on xxxx    ......
  1. 第二种service
service servicename    ......
  1. 第三种import
import *.rc

而这三种模式的关键字都是属于SECTION类型的,所以我们需要看一下parse_new_section函数里对这三种关键字的解析函数指针做了赋值。

parse_new_section函数的源码内容如下:

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

果然,针对这三种关键字,其解析函数parse_line也被赋予了不同的值。我们挨个分析一下。


K_import

import模式主要是导入新的rc配置文件,所以我们看一下parse_import函数的具体实现:

struct import {    struct listnode list;    const char *filename;};void list_add_tail(struct listnode *head, struct listnode *item){    item->next = head;    item->prev = head->prev;    head->prev->next = item;    head->prev = item;}void parse_import(struct parse_state *state, int nargs, char **args){    struct listnode *import_list = state->priv;    struct import *import;    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 handing import on line '%d' in '%s'\n", state->line, state->filename);        return;    }    import = calloc(1, sizeof(struct import));    import->filename = strdup(conf_file);    list_add_tail(import_list, &import->list);    INFO("found import '%s', adding to import list", import->filename);}

代码还是比较简单的,一句话概括就是将import导入的rc文件链接到state->priv链表中。state->priv链表链接了所有的import文件,最后会在parse_config函数的parser_done标签中进行处理。


K_on

K_on关键字代表了trigger模式,跟trigger模式相关的函数只要是parse_action和parse_line_action。

action结构体源码如下(/system/core/init/init.h):

struct action {    /* node in list of all actions */    struct listnode alist;    /* node in the queue of pending actions */    struct listnode qlist;    /* node in the list of actions for a trigger */    struct listnode tlist;    unsigned hash;    const char *name;    // command链表,以zygote为例,它有4个onrestart option,所以它对应会创建4个command结构体    struct listnode commands;    struct command *current;};struct command{    /* list of commands in an action */    struct listnode clist;    int (*func)(int nargs, char **args);    int nargs;    char *args[1];};

接下来,看一下parse_action和parse_line_action函数的具体实现。

void list_init(struct listnode *node){    node->next = node;    node->prev = node;}void list_add_tail(struct listnode *head, struct listnode *item){    item->next = head;    item->prev = head->prev;    head->prev->next = item;    head->prev = item;}static void *parse_action(struct parse_state *state, int nargs, char **args){    struct action *act;    if (nargs < 2) {        parse_error(state, "actions must have a trigger\n");        return 0;    }    if (nargs > 2) {        parse_error(state, "actions may not have extra parameters\n");        return 0;    }    act = calloc(1, sizeof(*act));    act->name = args[1];    list_init(&act->commands);    list_init(&act->qlist);    list_add_tail(&action_list, &act->alist);    return act;}static void parse_line_action(struct parse_state *state, int nargs, char **args){    struct command *cmd;    struct action *act = state->context;    int (*func)(int nargs, char **args);    int kw, n;    if (nargs == 0) {        return;    }    kw = loopup_keyword(args[0]);    if (!kw_is(kw, COMMAND)) {        parse_error(state, "invalid command '%s'\n", args[0]);        return;    }    n = kw_nargs(kw);    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);    cmd->func = kw_func(kw);    cmd->nargs = nargs;    memcpy(cmd->args, args, sizeof(char*) * nargs);    list_add_tail(&act->commands, &cmd->clist);}

在parse_line_action构造command结构体的时候,有一个关键函数kw_func,它返回函数指针决定了command的具体实现。我们来看一下kw_func的函数源码:

#define kw_nargs(kw) (keyword_info[kw].nargs)#define kw_func(kw) (keyword_info[kw].func)struct {    const char *name;    int (*func)(int nargs, char **args);    unsigned char nargs;    unsigned char flags;} keyword_info[KEYWORK_COUNT] = {    [K_UNKNOWN] = {"unknown", 0, 0, 0},    #include "keywords.h"};

这keyword_info结构体的初始化是很神奇的,希望大家也能感受到它的精妙之处。可以看到,代码里初始化了一个成员K_UNKNOWN之后,后面跟了一句#include “keywords.h”。这就是神奇的地方,因为代码编译过程中,include引用的内容会在当前位置进行展开,也就是说keyword_info其它成员的初始化是在”keywords.h”文件中完成的。我们来看一下keywords.h文件源码():

#ifndef KEYWORDint do_chroot(int nargs, char **args);int do_chdir(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_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_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_rm(int nargs, char **args);int do_rmdir(int nargs, char **args);int do_setcon(int nargs, char **args);int do_setenforce(int nargs, char **args);int do_setkey(int nargs, char **args);int do_setprop(int nargs, char **args);int do_setrlimit(int nargs, char **args);int do_setsebool(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_wait(int nargs, char **args);#define __MAKE_KEYWORD_ENUM__#define KEYWORD(symbol, flags, nargs, func) K_##symbol,enum {    K_UNKNOWN,#endif    KEYWORD(capability,  OPTION,  0, 0)    KEYWORD(chdir,       COMMAND, 1, do_chdir)    KEYWORD(chroot,      COMMAND, 1, do_chroot)    KEYWORD(class,       OPTION,  0, 0)    KEYWORD(class_start, COMMAND, 1, do_class_start)    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)    KEYWORD(class_reset, COMMAND, 1, do_class_reset)    KEYWORD(console,     OPTION,  0, 0)    KEYWORD(critical,    OPTION,  0, 0)    KEYWORD(disabled,    OPTION,  0, 0)    KEYWORD(domainname,  COMMAND, 1, do_domainname)    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(insmod,      COMMAND, 1, do_insmod)    KEYWORD(import,      SECTION, 1, 0)    KEYWORD(keycodes,    OPTION,  0, 0)    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)    KEYWORD(mount_all,   COMMAND, 1, do_mount_all)    KEYWORD(mount,       COMMAND, 3, do_mount)    KEYWORD(on,          SECTION, 0, 0)    KEYWORD(oneshot,     OPTION,  0, 0)    KEYWORD(onrestart,   OPTION,  0, 0)    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)    KEYWORD(restart,     COMMAND, 1, do_restart)    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)    KEYWORD(rm,          COMMAND, 1, do_rm)    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)    KEYWORD(seclabel,    OPTION,  0, 0)    KEYWORD(service,     SECTION, 0, 0)    KEYWORD(setcon,      COMMAND, 1, do_setcon)    KEYWORD(setenforce,  COMMAND, 1, do_setenforce)    KEYWORD(setenv,      OPTION,  2, 0)    KEYWORD(setkey,      COMMAND, 0, do_setkey)    KEYWORD(setprop,     COMMAND, 2, do_setprop)    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)    KEYWORD(setsebool,   COMMAND, 2, do_setsebool)    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(trigger,     COMMAND, 1, do_trigger)    KEYWORD(symlink,     COMMAND, 1, do_symlink)    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)    KEYWORD(user,        OPTION,  0, 0)    KEYWORD(wait,        COMMAND, 1, do_wait)    KEYWORD(write,       COMMAND, 2, do_write)    KEYWORD(copy,        COMMAND, 2, do_copy)    KEYWORD(chown,       COMMAND, 2, do_chown)    KEYWORD(chmod,       COMMAND, 2, do_chmod)    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)    KEYWORD(ioprio,      OPTION,  0, 0)#ifdef __MAKE_KEYWORD_ENUM__    KEYWORD_COUNT,};#undef __MAKE_KEYWORD_ENUM__#undef KEYWORD#endif

因为KEYWORD已经在init_parser.c文件中定义了,所以从#ifndef KEYWORD到#endif之间的代码是不需要care的。KEYWORD的定义如下(/system/core/init/init_parser.c):

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

这里结合KEYWORD(class_start, COMMAND, 1, do_class_start)来讲解一下这个宏的作用。

  1. 预处理运算符: #(单井号)——字符串化运算符。##(双井号)——连接运算符。
  2. K_##symbol == K_class_start。#symbol == class_start
  3. KEYWORD(class_start, COMMAND, 1, do_class_start) == [K_class_start] = {class_start, do_class_start, 2, COMMAND}

经过上述分析,我们经已经清楚了keyword_info数组的初始化过程。同时,也知道了相应的cmd的func函数指针指向的具体函数。func函数的具体实现在/system/core/init/builtins.c文件里,感兴趣的同学可以自行阅读源码。

command执行

那最终这些command是如何执行的呢?

因为on ×××是基于某个事件触发,init.rc中列举的事件包括:

  • on early-init
  • on init
  • on fs
  • on post-fs
  • on post-fs-data
  • on boot
  • on nonencrypted
  • on charger
  • on property:key=value

那init.rc这些trigger是在什么时间点触发呢?其实这些trigger的触发顺序是由init.rc里面添加它们到action queue的顺序决定的。相关代码如下:

void action_for_each_trigger(const char *trigger, void (*func)(struct action *act)){    struct listnode *node;    struct action *act;    list_for_each(node, &action_list) {        act = node_to_item(node, struct action, alist);        if (!strcmp(act->name, trigger)) {            func(act);        }    }}// func是函数指针,具体实现函数为action_add_queue_tailvoid action_add_queue_tail(struct action *act){    if (list_empty(&act->qlist)) {        list_add_tail(&action_queue, &act->qlist);    }}action_for_each_trigger("early-init", action_add_queue_tail);action_for_each_trigger("init", action_add_queue_tail);if (!is_charger) {    action_for_each_trigger("early-fs", action_add_queue_tail);    action_for_each_trigger("fs", action_add_queue_tail);    action_for_each_trigger("post-fs", action_add_queue_tail);    action_for_each_trigger("post-fs-data", action_add_queue_tail);}if (is_charger) {    action_for_each_trigger("charger", action_add_queue_tail);} else {    action_for_each_trigger("early-boot", action_add_queue_tail);    action_for_each_trigger("boot", action_add_queue_tail);}

在init.c中以合适的顺序添加到action queue里面之后,在init.c main函数的最后会依次从action queue中取出这些action,顺序执行。

for(;;) {    int nr, i, timeout = -1;    execute_one_command();}

K_service

下文会重点介绍service的解析,这里就略过了。


service

上述重点在于SECTION类型中的K_on和K_import关键字解析。接下来,我们要看一下K_service关键字的解析实现了,以zygote service为例。

zygote对应的service section内容是(/system/core/rootdir/init.rc):

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server    class main    socket zygote stream 660 root system    onrestart write /sys/android_power/request_state wake    onrestart write /sys/power/state on    onrestart restart media    onrestart restart netd

解析service时,用到了parse_service和parse_line_service这两个函数,在分别介绍它们之前,我们先来学习一下service的数据结构。


service结构体

init进程中使用了一个叫做service的结构体来保存与service section相关的信息,结构体的定义如下(system/core/init/init.h):

struct service {    /* list of all service */    struct listnode slist;    const char *name;   /* service的名字,比如"zygote" */    const char *classname;  /* service所属的class的名字,默认是"default" */    unsigned flags; /* service的属性 */    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;    char *seclabel;    struct socketinfo *sockets;    // service一般运行在一个独立的进程中,envvars用来描述创建这个进程时所需的环境变量信息    struct svcenvinfo *envvars;    struct action onrestart;    int *keycodes;    int nkeycodes;    int keychord_id;    int ioprio_class;    int ioprio_pri;    int nargs;  // 参数个数    char *args[1];  // 用于存储参数}

service一共有5个属性,分别为:

  1. SVC_DISABLED:不随class自动启动。
  2. SVC_ONESHOT:退出后不需要重启,也就是说这个service只启动一次就可以了。
  3. SVC_RUNNING:正在运行,这是service的状态。
  4. SVC_CONSOLE:该service需要使用控制台。
  5. SVC_CRITICAL:如果在规定的时间内该service不断重启,则系统会重启并进入恢复模式。

service的结构体虽然成员很多,但是比较好理解。但是,例如像zygote这种service,其中的onrestart该怎么表示呢?其实是用了action这个结构体。
了解了这些基本的结构体之后,我们接下来看一下parse_service和parse_line_service的具体实现。


parse_service

parse_service的源码位置是:/system/core/init/init_parser.c,源码如下:

static int valid_name(const char *name){    if (strlen(name) > 16) {        return 0;    }    while (*name) {        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {            return 0;        }        name++;    }    return 1;}#define list_for_each(node, list) \    for(node = (list)->next; node != (list); node = node->next)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;}static void *parse_service(struct parse_state *state, int nargs, char **args){    struct service *svc;    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;    }    svc = 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 = calloc(1, sizeof(*svc), sizeof(char*) * nargs);    if (!svc) {        parse_error(state, "out of memory\n");        return 0;    }    svc->name = args[1];    svc->classname = "default";    memcpy(svc->args, args + 2, sizeof(char*) * nargs);    svc->args[nargs] = 0;    svc->nargs = nargs;    svc->onrestart.name = "onrestart";    list_init(&svc->onrestart.commands);    list_add_tail(&service_list, &svc->slist);}

parse_service函数只是搭建了一个service的架子,具体的内容尚需由后面的解析函数来填充。接下来看一下另外一个解析函数parse_line_service。

parse_line_service

parse_line_service的源码位置:/system/core/init/init_parser.c,源码内容如下:

static void parse_line_service(struct parse_state *state, int nargs, char **args){    struct service *svc = state->context;    struct command *cmd;    int i, kw, kw_nargs;    if (nargs == 0) {        return;    }    svc->ioprio_class = IoSchedClass_NONE;    kw = loopup_keyword(args[0]);    switch(kw) {    case K_capability:        break;    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, "opprio 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|dle> <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 = 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:        nagrs --;        args ++;        kw = loopup_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 = 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:        struct svcenvinfo *ei;        if (nargs < 2) {            parse_error(state, "setenv option requires name and value arguments\n");            break;        }        ei = 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:        struct socketinfo *si;        if (nargs < 4) {            parse_error(state, "socket option requires name, type, perm arguments\n");        }        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;        }        si = 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]);        }        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_seckable:        if (nargs != 2) {            parse_error(state, "seclable option requires a label string\n");        } else {            svc->seclable = args[1];        }        break;    default:        parse_error(state, "invalid option '%s'\n", args[0]);    }}

parse_line_service将根据配置文件的内容填充service结构体,那么,zygote解析完后的结果为:

TODO:需要画图


init控制service

了解了service的数据结构和组装,接下来,我们需要了解init是如何控制service的。

init.rc中有这样一条命令:

class_start defaultclass_start mainclass_start core

class_start标识一个COMMAND,对应的处理函数为do_class_start,它位于on boot的范围内。当init进程执行到下面几句话时,do_class_start就会被执行了。

action_for_each_trigger("boot", action_add_queue_tail);

下面我们来看一下do_class_start函数,函数位置:/system/core/init/builtins.c:

int do_class_start(int nargs, char **args){    /**     * Starting a class does not start services     * which are explicitly disabled. They must     * be started individually.     */    service_for_each_class(args[1], service_start_if_not_disabled);    return 0;}static void service_start_if_not_disabled(struct service *svc){    if (!(svc->flags & SVC_DISABLED)) {        service_start(svc, NULL);    }}

zygote这个service的classname值是main,flags标志位不是disable,所以会调用service_start来启动zygote。
service_start的函数实现如下(/system/core/init/init.c):

static const char *ENV[32];int add_environment(const char *key, const char *val){    int n;    for (n = 0; n < 31; n ++) {        if (!ENV[n]) {            size_t len = strlen(key) + strlen(val) + 2;            char *entry = malloc(len);            snprintf(entry, len, "%s=%s", key, val);            ENV[n] = entry;            return 0;        }    }}#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"#define ANDROID_SOCKET_DIR "/dev/socket"static void publish_socket(const char *name, int fd){    char key[64] = ANDROID_SOCKET_ENV_PREFIX;    char val[64];    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, name, sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));    snprintf(val, sizeof(val), "%d", fd);    add_environment(key, val);    /* make sure we don't close-on-exec */    fcntl(fd, F_SETFD, 0);}void notify_service_state(const char *name, const char *state){    char pname[PROP_NAME_MAX];    int len = strlen(name);    if ((len + 10) > PROP_NAME_MAX) {        return;    }    snprintf(pname, sizeof(pname), "init.svc.%s", name);    property_set(pname, state);}void service_start(struct service *svc, const char *dynamic_args){    struct stat s;    pid_t pid;    int needs_console;    int n;    char *scon = NULL;    int rc;    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART));    svc->time_started = 0;    if (svc->flags & SVC_RUNNING) {        return;    }    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;    if (needs_console && (!have_console)) {        ERROR("service '%s' requires console\n", svc->name);        svc->flags |= SVC_DISABLED;        return;    }    /**     * service一般运行于另外一个进程中,这个进程也是init的子进程     * 所以,启动service前需要判断对应的可执行文件是否存在     */    if (stat(svc->args[0], &s) != 0) {        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);        svc->flags |= SVC_DISABLED;        return;    }    NOTICE("starting '%s'\n", svc->name);    pid = fork();    if (pid == 0) {        // pid为0,表示运行在子线程中        struct socketinfo *si;        struct svcenvinfo *ei;        char tmp[32];        int fd, sz;        umask(077);        if (properties_inited()) {            // 得到属性存储空间的信息并加到环境变量中            get_property_workspace(&fd, &sz);            sprintf(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);        // 根据socketinfo创建socket        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);            if (s >= 0) {                publish_socket(si->name, s);            }        }    }    // ......省略设置uid,gid代码    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 {        // ......省略动态参数的execve执行代码    }    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;    /**     * 每一个service都有一个属性     * 例如:zygote的属性为init.svc.zygote,设置当前属性值为running     * /    if (properties_inited()) {        notify_service_state(svc->name, "running");    }}

通过上述代码,我们可以知道,原来service的启动是通过fork和execve共同实现的。

  相关解决方案