Android系统启动2————Android初始化语言概述和解析

Android系统启动2————Android初始化语言概述和解析

一.概述

在Android初始化语言包含了4种类型的声明,Actions(行动),Commands(命令)、Services(服务)和Options(选项)。

所以这些都是以行为单位,各种记号由空格隔开。反斜杠可用于在记号间插入空格,双引号也可以用于防止字符串被空格分隔成多个记号。行末的反斜杠用于折行。
注释行以井号(#)开头(允许以空格开头)。

Actions和Services声明一个新的分组。所有的命令或选项都属于最近申明的分组。位于第一个分组之前的命令或选项将会被忽略。
Actions和Services有唯一的名字。如果有重名的情况,第二个申明的将会被作为错误忽略。

二.Actions

Actions其实就是一序列的Commands(命令)。Actions都有一个trigger(触发器),它被用于决定action的执行时间。当一个符合action触发条件的事件发生时,action会被加入到执行队列的末尾,除非它已经在队列里了。
队列中的每一个action都被依次提取出,而这个action中的每个command(命令)都将被依次执行。Init在这些命令的执行期间还控制着其他的活动(设备节点的创建和注销、属性的设置、进程的重启)。

1.Actions的形式
on <trigger>  
   <command>  
   <command>  
   <command>

示例

on boot  
   ifup lo  
   hostname localhost  
   domainname localdomain  

上面的示例中,boot是触发器,下面的三行是command

2.触发器
触发器含义
boot在init执行后出发,也就是在init.rc被装载后执行该trigger
<name>=<value>当属性被设置成时被触发
device-added-<path>当设备节点被添加时触发
device-removed-<path>当设备节点被移除时添加
service-exited-<name>会在一个特定的服务退出时触发
3.命令
命令含义
exec <path> [<argument> ]*创建和执行一个程序(<path>)。在程序完全执行前,init将会阻塞。 尽量避免使用 ,它可能会引起init执行超时
export <name> <value>在全局环境中将 <name>变量的值设为<value>
ifup <interface>启动网络接口
import <filename>指定要解析的其他配置文件
hostname <name>设置主机名
chdir <directory>改变工作目录
chmod <octal-mode><path>改变文件的访问权限
chown <owner><group> <path>更改文件的所有者和组
chroot <directory>改变处理根目录
class_start<serviceclass>启动所有指定服务类下的未运行服务
class_stop<serviceclass>停止指定服务类下的所有已运行的服务。
domainname <name>设置域名
insmod <path>加载<path>指定的驱动模块
mkdir <path> [mode][owner] [group]创建一个目录<path> ,可以选择性地指定mode、owner以及group。如果没有指定,默认的权限为755,并属于root用户和 root组。
mount <type> <device> <dir> [<mountoption> ]*试图在目录<dir>挂载指定的设备
setkey保留,暂时未用
setprop <name><value>将系统属性<name>的值设为<value>
setrlimit <resource> <cur> <max>设置<resource>的rlimit (资源限制)
start <service>启动指定服务(如果此服务还未运行)
stop<service>停止指定服务(如果此服务在运行中)
symlink <target> <path>创建一个指向<path>的软连接<target>
sysclktz <mins_west_of_gmt>设置系统时钟基准
trigger <event>触发一个事件。用于Action排队
wait <path> [<timeout> ]等待一个文件是否存在,当文件存在时立即返回,或到<timeout>指定的超时时间后返回,如果不指定<timeout>,默认超时时间是5秒。
write <path> <string> [ <string> ]*向<path>指定的文件写入一个或多个字符串

三.Services

Services(服务)是一个程序,他在初始化时启动,并在退出时重启(可选)

1.Services 形式
   service <name> <pathname> [ <argument> ]*  //<service的名字><执行程序路径><传递参数>  
          <option>  
          <option>  

示例

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

Services的选项是服务的修饰符,可以影响服务如何以及怎样运行

2.选项
  • critical,明这是一个非常重要的服务。如果该服务4分钟内退出大于4次,系统将会重启并进入 Recovery (恢复)模式。
  • disabled,表明这个服务不会同与他同trigger (触发器)下的服务自动启动。该服务必须被明确的按名启动。
  • setenv <name><value>,在进程启动时将环境变量<name>设置为<value>。
  • socket <name><type> <perm> [ <user> [ <group> ] ], 创建一个unix域的名为/dev/socket/<name> 的套接字,并传递它的文件描述符给已启动的进程。<type> 必须是 “dgram”,“stream” 或"seqpacket"。用户和组默认是0。
  • user <username>,在启动这个服务前改变该服务的用户名。
  • group <groupname> [<groupname> ]*,在启动这个服务前改变该服务的组名。除了(必需的)第一个组名,附加的组名通常被用于设置进程的补充组(通过setgroups函数),档案默认是root。
  • oneshot, 服务退出时不重启。
  • class <name>,指定一个服务类。所有同一类的服务可以同时启动和停止。如果不通过class选项指定一个类,则默认为"default"类服务。
  • onrestart,当服务重启,执行一个命令

四.分析rc文件

在Android启动过程中,通过下面代码来分析rc文件.
源码版本:Android 8.0

 parser.ParseConfig("/init.rc");
1.Parser类

Parser类的定义在parser.h中

目录:system/core/init/init_parser.h

class SectionParser {
public:
    virtual ~SectionParser() {
    }
    virtual bool ParseSection(const std::vector<std::string>& args,
                              std::string* err) = 0;
    virtual bool ParseLineSection(const std::vector<std::string>& args,
                                  const std::string& filename, int line,
                                  std::string* err) const = 0;
    virtual void EndSection() = 0;
    virtual void EndFile(const std::string& filename) = 0;
};

class Parser {
public:
    static Parser& GetInstance();
    void DumpState() const;
    bool ParseConfig(const std::string& path);
    void AddSectionParser(const std::string& name,
                          std::unique_ptr<SectionParser> parser);
    void set_is_system_etc_init_loaded(bool loaded) {
        is_system_etc_init_loaded_ = loaded;
    }
    void set_is_vendor_etc_init_loaded(bool loaded) {
        is_vendor_etc_init_loaded_ = loaded;
    }
    void set_is_odm_etc_init_loaded(bool loaded) {
        is_odm_etc_init_loaded_ = loaded;
    }
    bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
    bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
    bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }

private:
    Parser();

    void ParseData(const std::string& filename, const std::string& data);
    bool ParseConfigFile(const std::string& path);
    bool ParseConfigDir(const std::string& path);

    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
    bool is_system_etc_init_loaded_ = false;
    bool is_vendor_etc_init_loaded_ = false;
    bool is_odm_etc_init_loaded_ = false;
};

parser包含一个比较重要的成员变量

  • section_parsers_,是负责解析语法的,在初始化的时候添加了三个解析类ServiceParser、ActionParser、ImportParser,通过AddSectionParser函数添加

目录;system/core/init/init_parser.cpp

void Parser::AddSectionParser(const std::string& name,
                              std::unique_ptr<SectionParser> parser) {
    section_parsers_[name] = std::move(parser);
}

2.parser.ParseConfig

分析完Parser类,我们继续来看看ParseConfig,即Parser对外提供的解析.rc方法

目录:system/core/init/init_parser.cpp

bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        return ParseConfigDir(path);
    }
    return ParseConfigFile(path);//继续追踪
}

bool Parser::ParseConfigFile(const std::string& path) {
    LOG(INFO) << "Parsing file " << path << "...";
    Timer t;
    std::string data;
    if (!read_file(path, &data)) { //打开.rc文件,并返回内容
        return false;
    }

    data.push_back('\n'); // TODO: fix parse_config.
    ParseData(path, data); //核心函数,主要用来分析.rc文件内容
    for (const auto& sp : section_parsers_) {
        sp.second->EndFile(path);
    }

    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
    return true;
}

下面分析重点内容ParseData

void Parser::ParseData(const std::string& filename, const std::string& data) {
    //TODO: Use a parser with const input and remove this copy
    std::vector<char> data_copy(data.begin(), data.end());
    data_copy.push_back('\0');

    parse_state state;
    state.filename = filename.c_str();
    state.line = 0;
    state.ptr = &data_copy[0];
    state.nexttoken = 0;

    SectionParser* section_parser = nullptr;
    std::vector<std::string> args;//存放token的容器

    for (;;) {
    	//相当于词法分析器,重点代码①
        switch (next_token(&state)) {
        case T_EOF://.rc文件分析完毕
            if (section_parser) {
                section_parser->EndSection();
            }
            return;
        case T_NEWLINE://分析每一行的代码
            state.line++;
            if (args.empty()) { //为空
                break;
            }
            //section_parsers_ 匹配规则
            if (section_parsers_.count(args[0])) {
                if (section_parser) {
                    section_parser->EndSection();
                }
                // 根据参数获取解析器②
                section_parser = section_parsers_[args[0]].get();
                std::string ret_err;
                // 解析内容,重点代码③
                if (!section_parser->ParseSection(args, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                    section_parser = nullptr;
                }
            } else if (section_parser) {
                std::string ret_err;
                if (!section_parser->ParseLineSection(args, state.filename,
                                                      state.line, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                }
            }
            args.clear();
            break;
        case T_TEXT://处理每一个token
            args.emplace_back(state.text);
            break;
        }
    }
}

分析一下parse_config方法的原理。在for循环中next_token方法不断从init.rc文件中获取token。

token,就是一种编程语言的最小 单元,也就是不可再分。例如,对于传统的编程语言,if、then等关键字、变量名等标识符都属于一个token。而对于init.rc文件来 说,import、on、以及触发器的参数值,都属于一个token。

一个完整的编译器最开始需要进行词法和语法分析,词法分析就是从源代码中挑出一个个token,也就是说,词法分析器的返回值是 Token,而语法分析器的输入就是词法分析器的输出。语法分析器需要分析一个个的token,而不是一个个的字符。由于init解析语言很简 单,所以就将词法和语法分析器放到了一起。词法分析器就是next_token函数,而语法分析器就是T_NEWLINE分支中的代码,更详细的说是section_parser->ParseLineSection。

3.next_token

下面是next_token()的调用

next_token(&state)

先分析传入的参数,类型是parse_state的结构体,定义在parser.h

目录:system/core/init/parser.h

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

然后继续看看next_token的实现,他的实现在parser.cpp

目录:system/core/init/parser.cpp

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

	//过滤掉结束符 \r \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; //开始解析token
        }
    }

textdone: //解析完成,返回给ParseConfig
    state->ptr = x; //更新下次需要解析的字符串位置
    *s = 0;
    return T_TEXT;
text:
    state->text = s = x; //将解析出来的token存放在text中
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;
}

通过上面的next_token解析出来一个个tokent,接下来来就是分析token

4.section_parsers_ 获取解析器

先回顾一下ParseData的内容,现在就可解析代码含义了

目录:system/core/init/init_parser.cpp ParseData函数

case T_NEWLINE: //解析出来
            state.line++;
            if (args.empty()) { 
                break;
            }
            //使用count,返回的是被查找元素的个数。如果有,返回1;否则,返回0。
            //即查找该Actions/Services是否有对应的加载器
            if (section_parsers_.count(args[0])) {
                if (section_parser) {
                    section_parser->EndSection();
                }
                //获取section_parser
                section_parser = section_parsers_[args[0]].get();
                std::string ret_err;
                //调用section_parser->ParseSection进行初始化  重点代码下面分析
                if (!section_parser->ParseSection(args, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                    section_parser = nullptr;
                }
            } else if (section_parser) {
                std::string ret_err;
                //进行解析 重点代码下面分析
                if (!section_parser->ParseLineSection(args, state.filename,
                                                      state.line, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                }
            }
            args.clear();
            break;
        case T_TEXT:
            args.emplace_back(state.text);//加入到args容器的尾部
            break;

获取section_parser语法分析器

ection_parser = section_parsers_[args[0]].get();获取到成员分析器

我们前面在分析Parser类说过,他有一个section_parsers_成员变量,在init.cpp通过AddSectionParser来添加对应的语法解析器。
调用

目录;system/core/init/init.cpp

    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm)); //service解析器
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am)); //Actions解析器
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

实现

目录:system/core/init/init_parser.cpp

void Parser::AddSectionParser(const std::string& name,
                              std::unique_ptr<SectionParser> parser) {
    section_parsers_[name] = std::move(parser);
}

我们现在主要来看看service解析器的实现

5.ServiceParser

ServiceParser的定义,在 service.h

目录: system/core/init/service.h

class ServiceParser : public SectionParser {
public:
    ServiceParser() : service_(nullptr) {
    }
    bool ParseSection(const std::vector<std::string>& args,
                      std::string* err) override;
    bool ParseLineSection(const std::vector<std::string>& args,
                          const std::string& filename, int line,
                          std::string* err) const override;
    void EndSection() override;
    void EndFile(const std::string&) override {
    }
private:
    bool IsValidName(const std::string& name) const;

    std::unique_ptr<Service> service_;
};

重点来看ParseSection和ParseLineSection两个方法

首先看看ParseSection方法

目录:system/core/init/service.c

bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                 std::string* err) {
    if (args.size() < 3) {
        *err = "services must have a name and a program";
        return false;
    }

    const std::string& name = args[1];
    if (!IsValidName(name)) {
        *err = StringPrintf("invalid service name '%s'", name.c_str());
        return false;
    }

    std::vector<std::string> str_args(args.begin() + 2, args.end());//获取Service的参数
    service_ = std::make_unique<Service>(name, str_args);//创建对应的Service类
    return true;
}

//Service的构造方法
Service::Service(const std::string& name, const std::vector<std::string>& args)
    : name_(name), //确定service的名字
      classnames_({"default"}),
      flags_(0),
      pid_(0),
      crash_count_(0),
      uid_(0),
      gid_(0),
      namespace_flags_(0),
      seclabel_(""),
      keychord_id_(0),
      ioprio_class_(IoSchedClass_NONE),
      ioprio_pri_(0),
      priority_(0),
      oom_score_adjust_(-1000),
      args_(args) {
    onrestart_.InitSingleTrigger("onrestart"); //设置重启
}

再来分析ParseLineSection

目录:system/core/init/service.c

bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
                                     const std::string& filename, int line,
                                     std::string* err) const {
    return service_ ? service_->ParseLine(args, err) : false; //
}

bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
    if (args.empty()) {
        *err = "option needed, but not provided";
        return false;
    }

	//查看OptionParserMap的构造函数
    static const OptionParserMap parser_map;
    //找出该function对应的方法
    auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);

    if (!parser) {
        return false;
    }

	//执行该方法
    return (this->*parser)(args, err);
}

//OptionParserMap的构造函数
class Service::OptionParserMap : public KeywordMap<OptionParser> {
public:
    OptionParserMap() {
    }
private:
    Map& map() const override;
};

Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    //将对应的字段转换成对应的方面
    static const Map option_parsers = {
        {"capabilities",
                        {1,     kMax, &Service::ParseCapabilities}},
        {"class",       {1,     kMax, &Service::ParseClass}},
        {"console",     {0,     1,    &Service::ParseConsole}},
        {"critical",    {0,     0,    &Service::ParseCritical}},
        {"disabled",    {0,     0,    &Service::ParseDisabled}},
        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
        {"ioprio",      {2,     2,    &Service::ParseIoprio}},
        {"priority",    {1,     1,    &Service::ParsePriority}},
        {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
        {"oom_score_adjust",
                        {1,     1,    &Service::ParseOomScoreAdjust}},
        {"namespace",   {1,     2,    &Service::ParseNamespace}},
        {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
        {"setenv",      {2,     2,    &Service::ParseSetenv}},
        {"socket",      {3,     6,    &Service::ParseSocket}},
        {"file",        {2,     2,    &Service::ParseFile}},
        {"user",        {1,     1,    &Service::ParseUser}},
        {"writepid",    {1,     kMax, &Service::ParseWritepid}},
    };
    // clang-format on
    return option_parsers;
}

在解析完所有Service后,会调用EndSection方法

void ServiceParser::EndSection() {
    if (service_) {
        ServiceManager::GetInstance().AddService(std::move(service_));
    }
}
void ServiceManager::AddService(std::unique_ptr<Service> service) {
    Service* old_service = FindServiceByName(service->name());
    if (old_service) {
        LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
        return;
    }
    services_.emplace_back(std::move(service)); //将Service加入到Service链表中,并根据之前的即内容进行填充
}

五.小结

  • 在init进程中将rc文件传入,并传入对应的解析器
  • 在init_parser利用parser对rc文件进行解析
  • 解析分为两个步骤:词法分析和语法分析
  • 词法分析,将rc文件里的字符分割成一个个对应的token
  • 语法分析,将一个个token放容器中,调用对应的解析器创建Service/Actions,然后根据对应的token在map中映射为对于的方法,进行调用。

六.参考资料

Android的init过程(二):初始化语言(init.rc)解析
Android P on property属性无法trigger流程分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值