Android init启动分析(2)init.rc解析处理

free-jdx 2021-04-15 15:05:49 5071
1. 前言

基于上一篇继续说明
https://www.ebaina.com/articles/140000012378

2. init.rc目录结构

system/core/rootdir/
├── Android.mk
├── etc
│   ├── hosts
│   ├── init.testmenu
│   └── mountd.conf
├── init.environ.rc.in
├── init.rc
├── init.trace.rc
├── init.usb.rc
├── init.zygote32_64.rc
├── init.zygote32.rc
├── init.zygote64_32.rc
├── init.zygote64.rc
└── ueventd.rc

Android 在system/core/init/readme.txt 文件中对rc脚本语言规则进行了说明.rc脚本主要包括了
Actions, Commands, Services, 和Options 4种类型声明.
Init.rc文件基本组成单位是section.,有Actions和Services、Import三种类型。

3. init.rc脚本格式

(1)Actions

Android rc脚本文件中Action,以关键字on开始,在关键字on和下一个关键字on之间为一个section;Action中关键字on后面的字符串是trigger,用于决定Action执行的时机.Action被执行时,其中的Command也会被逐一运行. Actions格式如下:

on <trigger>
   <command>
   <command>
   <command>

struct action {
    struct listnode alist;          /* actions链表节点 */
    struct listnode qlist;          /*等待执行的actions链表节点 */
    struct listnode tlist;      /* trigger 链表*/
    unsigned hash;
    const char *name; 
    struct listnode commands;   /* commands链表,用于挂载Action中所有的命令 */
    struct command *current;    /*当前正在被执行的command命令*/
};

(2)Service
Android rc脚本文件中Service是一个后台程序,Options是Service的一个参数项,用于决定Service运行时机以及如何运行,Service格式如下

service <name> <pathname> [ <argument> ]*
   <option>
   <option>
   ...

Inir进程中解析init.rc脚本中service section内容保存在struct service结构体实例中,结构体如下:

truct service {
    struct listnode slist;      //插入全局链表service_list的节点list
    const char *name;       //服务名称
    const char *classname;  //服务类名称,默认是“default“
    unsigned flags;        //服务keyword关键字类型 
    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;
    struct svcenvinfo *envvars;   //服务env环境变量
    struct action onrestart;   // 服务重启时需要执行的命令
    int *keycodes;            
    int nkeycodes;
    int keychord_id;
    int ioprio_class;
    int ioprio_pri;
    int nargs;    //参数个数
    char *args[1]; //服务值args[0]为脚本内容中第三个参数;args[1] = 0
}; 

(3)import 关键字
在一个.rc文件中引入其他的都rc文件.

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc

实际在根目录下存在的*.rc文件如下

init.common.rc
init.debug.rc
init.environ.rc
init.rc
init.recovery.sun50iw1p1.rc
init.sun50iw1p1.rc
init.sun50iw1p1.usb.rc
init.trace.rc
init.usb.rc
init.zygote32.rc
ueventd.rc
ueventd.sun50iw1p1.rc
init.rc import section

init.rc import section的解析主要在system/core/init/init_parser.c文件的parse_config()函数尾部,import解析内容会通过import结构体添加到import_list中,再次执行解析流程

static void parse_config(const char *fn, char *s)
{
... .... .... .....
parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
             ... .... .... .....
         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}
4. init.rc 解析

init.rc解析流程,解析的主函数在system/core/init/init_parser.c文件中:

static void parse_config(const char *fn, char *s)
{
    struct parse_state state;
struct listnode import_list;
... ... ... 
    list_init(&import_list);
    state.priv = &import_list;
    for (;;) {
        switch (next_token(&state)) {
        ... ... ... 
        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);
               }
... ... ... 
parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         ret = init_parse_config_file(import->filename);
    ... ... ... 
    }
}

parse_config主要完成以下步骤:

a.初始化state,将*.rc文件名称和文件内容分别保存在state. filename和state.ptr变量中。初始化import_list链表,链表指针保存在state.priv变量中;

b.通过next_token()函数检查每一行内容,分别返回T_EOF、T_NEWLINE和T_TEXT三种状态,分别为文件内容结束、文件内容换行和被空格隔开的参数三种类型;

c.针对T_NEWLINE类型,解析上一行脚本内容args[0]第一次参数,判断命令类型,如果是K_service、K_on和K_import类型,则调用parse_new_section进入新的section解析流程;

d.如果是K_service,调用parse_service( )动态创建struct service 类型内存区svc。初始化svc成员onrestart.name默认为"onrestart",初始化svc->onrestart.commands链表,用于挂载服务重启时执行的命令。将服务插入service_list全局链表中。最后将svc指针返回给state->context,state->parse_line指向parse_line_service( )函数,用于后面解析service section中的options参数。

e.如果是K_on,调用parse_action()动态初始化struct action 类型内存区act,初始化commands链表和qlist链表,将act插入到action_list全局链表中。最后将act指针返回给state->context,state->parse_line指向parse_line_action() 函数,用于解析Action section中的command命令,并插入act->commands链表中;

f.如果是K_import,调用parse_import(),动态创建struct import 类型内存区import,初始化filename,将import插入到import_list全局链表中。

5. init.rc 执行

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
free-jdx
红包 63 7 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
free-jdx
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区