Android启动流程分析(九) 解析init.rc的service

#############################################

本文为极度寒冰原创,转载请注明出处

#############################################

在分析完解析init.rc的action之后,剩下的一部分就是解析service了。

而解析service还是需要回到parse_config里面来。根据前面的知识,我们也可以很容易的知道在关键字为section的时候,会进入到parse_new_section。

这里会先执行parse_service,然后将service以及后面跟的option设置为执行parse_line:parse_line_service。

要理解service的解析流程的话,首先要关注的就是service的结构体。

struct service {
    /* list of all services */
    struct listnode slist;   // listnode slist

    const char *name;        // name
    const char *classname;   // 默认值为defult

    unsigned flags;          // 选项
    pid_t pid;               // Service所在进程的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;               // effective user ID
    gid_t gid;               // effective group ID
    gid_t supp_gids[NR_SVC_SUPP_GIDS];  // supplementary ids
    size_t nr_supp_gids;     // supp_gids的大小

    char *seclabel;

    struct socketinfo *sockets;  // 为service创建的Sockets
    struct svcenvinfo *envvars;  // 为service设置的环境变量

    struct action onrestart;  /* Actions to execute on restart. */

    /* keycodes for triggering this service via /dev/keychord */
    int *keycodes;
    int nkeycodes;
    int keychord_id;

    int ioprio_class;
    int ioprio_pri;

    int nargs;
    /* "MUST BE AT THE END OF THE STRUCT" */
    char *args[1];
}; /*     ^-------'args' MUST be at the end of this struct! */

这个结构体相比较而言就比较简单了,除了service的本身属性以外,对于数据结构方面就只有一个listnode。

struct listnode slist; 

这也就是说,在service的结构体中,这个结构体只会被加入一条链表而不是像action的两条链表。

另外需要注意的是,在service的结构体中,也维护了一个action的结构体,这就是说,在service中,也存在着一个action的commands的链表?

然后我们就来看看parse_service的函数

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc;  // 声明结构体
    if (nargs < 3) { // 如果service的参数小于三个的话,我们会认为service是个不正常的service。service最少的nargs也是3,分别为service关键字,service的名字,service启动的时候要执行的命令
        parse_error(state, "services must have a name and a program\n");
        return 0;
    }
    if (!valid_name(args[1])) {  //如果service的name为不标准的名字的话,含有其它的符号的话,我们会认为这个service是不规范的service。
        parse_error(state, "invalid service name '%s'\n", args[1]);
        return 0;
    }

    svc = service_find_by_name(args[1]); 。。// 会从已经存在的service_list里面去查找,是否已经有同名的service存在
    if (svc) {  // 如果发现有同名的service存在的话,则会返回error
        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
        return 0;
    }

    nargs -= 2;  // 去除service关键字与service的name
    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); // malloc这个结构体
    if (!svc) { // 如果malloc失败的话,提示out of memory
        parse_error(state, "out of memory\n");
        return 0;
    }
    svc->name = args[1];  // 设置service的name为service关键字后的第一个参数
    svc->classname = "default";  // 默认的classname为default
    memcpy(svc->args, args + 2, sizeof(char*) * nargs); // 将args剩余的参数复制到svc的args里面
    svc->args[nargs] = 0;  // 给args的最后一项设置为0
    svc->nargs = nargs;  // 参数的数目等于传进来的参数的数目
    svc->onrestart.name = "onrestart"; // 设置onrestart.name为onrestart
    list_init(&svc->onrestart.commands); // 初始化onrestart的链表
    list_add_tail(&service_list, &svc->slist);  // 将当前的service结构体加入到了service_list的链表里面
    return svc;
}

从上面我们知道,在执行完parse_service之后,会初始化service的一些属性,将service servicename之后的做为args进行保存。

另外,将这个解析出来的service加入到service_list的链表里面。

然后接下来,去执行的就是

state->parse_line = parse_line_service;

那我们像action,再来看看parse_line_service的操作

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 = lookup_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, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
        } else {    。。。。                                                                                                                                                                                                                                     ........                                                                                                                                                                                                                                                          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 = 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;                                                                                                                                                                                                                                                                                              ...............                                                                                                                                                                                                                                                                                 }

可以看到,parse_line_service的这个函数,主要就是将解析service的每一行,将其对应进不同的case里面,进行service结构体的填充。

可能这样讲,理解起来会有困难。

但是为了方便理解,我们从init.rc里面找个例子出来分析:

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

在parse_line_service的时候,会在

class的关键字的时候,执行到

    case K_class:
        if (nargs != 2) { // 判断是否是两个token,如果不是的话,格式错误
            parse_error(state, "class option requires a classname\n");
        } else {
            svc->classname = args[1];
        }
        break;

也就是将service的classname从"default"修改为“args[1]”

在user system的的option的时候,

会去执行

    case K_user:
        if (nargs != 2) {
            parse_error(state, "user option requires a user id\n");
        } else {
            svc->uid = decode_uid(args[1]);
        }
        break;

会将uid设置为system对应的uid

另外需要注意的是,在解析的过程中,会有一个比较重要的option是restart,来看一下当执行到这个关键字的时候的时候,会运行什么。

    case K_onrestart:
        nargs--;
        args++;
        kw = lookup_keyword(args[0]);
        if (!kw_is(kw, COMMAND)) {  // 判断是否是command,如果restart之后跟的不是command的话,就会返回error
            parse_error(state, "invalid command '%s'\n", args[0]);
            break;
        }
        kw_nargs = kw_nargs(kw);  // 获得当前command所需的参数
        if (nargs < kw_nargs) { // 如果传递的参数小于我们需要的参数的话,会返回error
            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); // 初始化command
        cmd->func = kw_func(kw);   // 将kw所包含的func赋值给cmd->func
        cmd->nargs = nargs;  // 将参数的个数保存为nargs
        memcpy(cmd->args, args, sizeof(char*) * nargs); // 将这些参数复制到cmd的args中
        list_add_tail(&svc->onrestart.commands, &cmd->clist); // 将这些command加入到service结构体的内部链表中
        break;

至此,我们分析了所有关于init.rc的解析问题。

在service的解析后,会生成一条链表保存service的结构体,然后service的结构体里面自己运行维护一个action。

这个action会包含所有的restatt包含的内容,也就是restart的option关键字后会包含要执行的command

这个的结构应该比action的要简单和明了的多。

分析完了解析,我们应该去看一下android是如何在启动的过程中去执行这些action和service的。

时间: 2024-10-26 17:05:27

Android启动流程分析(九) 解析init.rc的service的相关文章

Android启动流程分析(八) 解析init.rc的action

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 上一章讲述了android启动过程中,加载init.rc之后需要对其进行解析. 而解析又根据三个不同的SECTION来执行不同的初始化的文件,分别是parse_action,parse_service,parse_import. 那么,这一节,我们就从parse_action来讲

Android启动流程分析(十) action的执行和service的启动

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 在前面的文章分析完init.rc的解析以后,我们知道现在action按照init.c的main函数中的秩序,维护了一条qlist的链表,listnode为action_qlist service也维护了一条链表,listnode为service_list. 那么,在android

Android启动流程分析(一)

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# Android的启动流程绝大部分人都知道,但是大多数人都是这样描述的: Android启动,首先是启动Bootloader,然后挂载kernel,挂载完kernel之后,会启动android的init进程,init进程会去孵化Zygote,Zygote是android的重要支柱之

Android启动流程分析(十一) zygote的启动

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 前面的文章花了很大的篇幅去介绍了init进程如何去解析init.rc,如何去执行系统的一些服务. 那么,我们所说的zygote是怎么启动的呢?zygote又是具体负责了哪些工作呢? 本文我们来一探究竟. zygote在inir.rc中有如下的描述: service zygote

Android启动流程分析(十二) SystemServer

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# SystemServer也是系统的一个重要的守护进程,从SystemServer的进程中,我们看到了系统的各种关键的Service的启动. 另外,根据前面的zygote的分析,我们知道了systemServer在android的启动过程中是肯定要启动的. 因为在init.rc里面

Android 启动流程分析

原文:https://www.jianshu.com/p/a5532ecc8377 作者曾经在高通的Android性能组工作,主要工作是优化Android Application的启动时间. APP基础理论 要想优化App启动时间, 第一步就是了解App启动进程的工作原理. 有几个基础理论: Android Application与其他移动平台有两个重大不同点: 每个Android App都在一个独立空间里,意味着其运行在一个单独的进程中,拥有自己的VM, 被系统分配一个唯一的user ID A

Android启动流程分析(七) init.rc的解析

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# Init.rc的解析过程是笔者认为在android启动过程中,最复杂,最难理解的部分. 虽然它的内容很少,但是却包含了非常多的处理,接下来我们来慢慢的分析. 经过前面的分析,我们知道了read完init.rc的文件后,保存到了data的数组,传递到了parse_config的函数

Android启动流程分析(二) init进程的启动

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 分析init进程,首先要分析init进程是如何启动的, init的源码位于(system/core/init),我们先来看看init进程的android.mk 下面是从system/core/init模块拿出来的一段code: LOCAL_MODULE:= init LOCAL_

Android 4.4 Init进程分析二:init.rc文件的解析

国际惯例,我们先看一下源码: http://androidxref.com/4.4_r1/xref/system/core/init/init.c#1039 1 init_parse_config_file("/init.rc"); 在init进程的main()函数里,会调用init_parse_config_file()方法解析init.rc脚本,注意这里传递的参数是根目录下的 "/init.rc"文件路径. init_parse_config_file()方法定