Android O: init进程启动流程分析(阶段三)

在前面的博客Android O: init进程启动流程分析(阶段一)
Android O: init进程启动流程分析(阶段二)中,
我们已经介绍过了init进程启动时的一些准备工作。

本篇博客我们就来看看init进程启动时,解析init.rc文件相关的工作。


一、创建Parser并决定解析文件

int main(int argc, char** argv) {
    ..............
    //定义Action中的function_map_为BuiltinFuntionMap
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    //构造出解析文件用的parser对象
    Parser& parser = Parser::GetInstance();

    //为一些类型的关键字,创建特定的parser
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    parser.AddSectionParser("import", std::make_unique<ImportParser>());

    //判断是否存在bootscript
    std::string bootscript = GetProperty("ro.boot.init_rc", "");

    //如果没有bootscript,则解析init.rc文件
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");
        parser.set_is_system_etc_init_loaded(
                parser.ParseConfig("/system/etc/init"));
        parser.set_is_vendor_etc_init_loaded(
                parser.ParseConfig("/vendor/etc/init"));
        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    } else {
        //若存在bootscript, 则解析bootscript
        parser.ParseConfig(bootscript);
        parser.set_is_system_etc_init_loaded(true);
        parser.set_is_vendor_etc_init_loaded(true);
        parser.set_is_odm_etc_init_loaded(true);
    }
    .........
}
........

从上面的代码来看,8.0引入了bootScript的概念,
个人感觉这个应该是方便厂商的定制吧。

如果没有定义bootScript,那么init进程还是会解析init.rc文件。
init.rc文件是在init进程启动后执行的启动脚本,文件中记录着init进程需执行的操作。
此处解析函数传入的参数为“/init.rc”,解析的是运行时与init进程同在根目录下的init.rc文件。
该文件在编译前,定义于system/core/rootdir/init.rc中。

init.rc文件大致分为两大部分,一部分是以“on”关键字开头的动作列表(action list):

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000
    .........
    start ueventd

另一部分是以“service”关键字开头的服务列表(service list):

service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

借助系统环境变量或Linux命令,动作列表用于创建所需目录,以及为某些特定文件指定权限,
而服务列表用来记录init进程需要启动的一些子进程。

如上面代码所示,service关键字后的第一个字符串表示服务(子进程)的名称,
第二个字符串表示服务的执行路径。

二、ParseConfig函数
接下来,我们从Parser的ParseConfig函数入手,逐步分析整个文件解析的过程。

ParseConfig函数定义于system/core/init/ init_parser.cpp中:

扫描二维码关注公众号,回复: 1999893 查看本文章
bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        //传入参数为目录地址
        return ParseConfigDir(path);
    }
    //传入参数为文件地址
    return ParseConfigFile(path);
}

我们先来看看ParseConfigDir函数:

bool Parser::ParseConfigDir(const std::string& path) {
    ...........
    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
    ..........
    //递归目录,得到需要处理的文件
    dirent* current_file;
    std::vector<std::string> files;
    while ((current_file = readdir(config_dir.get()))) {
        // Ignore directories and only process regular files.
        if (current_file->d_type == DT_REG) {
            std::string current_path =
                android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
            files.emplace_back(current_path);
        }
    }

    // Sort first so we load files in a consistent order (bug 31996208)
    std::sort(files.begin(), files.end());
    for (const auto& file : files) {
        //容易看出,最终仍是调用ParseConfigFile
        if (!ParseConfigFile(file)) {
            LOG(ERROR) << "could not import file '" << file << "'";
        }
    }
    return true;
}

我们跟进一下ParseConfigFile函数:

bool Parser::ParseConfigFile(const std::string& path) {
    ........
    Timer t;
    std::string data;
    //读取路径指定文件中的内容,保存为字符串形式
    if (!read_file(path, &data)) {
        return false;
    }
    .........
    //解析获取的字符串
    ParseData(path, data);
    .........
    return true;
}

容易看出,ParseConfigFile只是读取文件的内容并转换为字符串。
实际的解析工作被交付给ParseData。

三、ParseData函数
ParseData函数定义于system/core/init/init_parser.cpp中,负责根据关键字解析出服务和动作。
动作与服务会以链表节点的形式注册到service_list与action_list中,
service_list与action_list是init进程中声明的全局结构体。

ParseData的关键代码下所示:

void Parser::ParseData(const std::string& filename, const std::string& data) {
    //TODO: Use a parser with const input and remove this copy
    //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;

    for (;;) {
        //next_token获取分割符,初始没有分割符时,进入T_TEXT分支
        switch (next_token(&state)) {
            case T_EOF:
                if (section_parser) {
                    //EOF,解析结束
                    section_parser->EndSection();
                }
                return;
            case T_NEWLINE:
                state.line++;
                if (args.empty()) {
                    break;
                }
                //在前文创建parser时,我们为service,on,import定义了对应的parser 
                //这里就是根据第一个参数,判断是否有对应的parser
                if (section_parsers_.count(args[0])) {
                    if (section_parser) {
                        //结束上一个parser的工作,
                        //将构造出的对象加入到对应的service_list与action_list中
                        section_parser->EndSection();
                    }

                    //获取参数对应的parser
                    section_parser = section_parsers_[args[0]].get();
                    std::string ret_err;
                    //调用实际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) {
                    //如果新的一行,第一个参数不是service,on,import
                    //则调用前一个parser的ParseLineSection函数
                    //这里相当于解析一个参数块的子项
                    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中
                args.emplace_back(state.text);
                break;
        }
    }
}

上面的代码看起来比较复杂,但实际上就是面向对象,根据不同的关键字,
使用不同的parser对象进行解析。

我们现在回忆一下init进程main函数中,创建parser的代码:

...........
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
...........

三种Parser均是继承SectionParser,具体的实现各有不同。
接下来,我们以比较常用的ServiceParser和ActionParser为例,
看看解析的结果如何处理。

3.1、ServiceParser
ServiceParser定义于system/core/init/service.cpp中。

从前面的代码我们知道,解析一个service块时,首先需要调用ParseSection函数,
接着利用ParseLineSection处理子块,解析完所有数据后,最后调用EndSection。
因此,我们着重看看ServiceParser的这三个函数。

3.1.1、ParseSection

bool ServiceParser::ParseSection(.....) {
    //参数检验
    .......

    //服务名
    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);
    return true;
}

ParseSection主要校验参数的有效性,并创建出Service结构体。

3.1.2、ParseLineSection

//注意这里已经在解析子项了
bool ServiceParser::ParseLineSection(......) const {
    //调用service对象的HandleLine
    return service_ ? service_->ParseLine(args, err) : false;
}

我们跟进一下ParseLine函数:

bool Service::ParseLine(.....) {
    //参数校验
    ........

    //OptionParserMap继承自keywordMap<OptionParser>
    static const OptionParserMap parser_map;

    //根据子项的内容,找到对应的处理函数
    //FindFunction利用OptionParserMap的map,根据参数找到对应的处理函数
    auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);

    if (!parser) {
        return false;
    }

    //调用对应的处理函数
    return (this->*parser)(args, err);
}

为了了解这部分内容,我们需要看看OptionParserMap中的map函数:

class Service::OptionParserMap : public KeywordMap<OptionParser> {
    ...........
    Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
        constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();

        //定义了各种关键子对应的处理函数
        static const Map option_parsers = {
            {"capabilities", {1,    kMax, &Service::ParseCapabilities}},
            {"class",        {1,    1,    &Service::ParseClass}},
            .....................
        };

        return option_parsers;
    }
    .......
}

我们以class对应的处理函数为例,看看对应的代码:

bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
    classnames_ = std::set<std::string>(args.begin() + 1, args.end());
    return true;
}

容易看出,这部分代码其实就是填充service对象对应的域。

因此,可以推断ParseLineSection函数的作用,
就是根据关键字填充Service对象。

3.1.3、EndSection
最后,我们来看看EndSection函数的流程:

//注意此时service对象已经构造完毕
void ServiceParser::EndSection() {
    if (service_) {
        ServiceManager::GetInstance().AddService(std::move(service_));
    }
}

我们继续跟进AddService函数:

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

    //将service对象加入到services_里
    services_.emplace_back(std::move(service));
}

从上面的一系列代码,我们可以看出ServiceParser的工作流程就是:
首先,根据第一行的名字和参数创建出service对象;
然后,根据子项的内容填充service对象;
最后,将创建出的service对象加入到vector类型的service链表中。

3.2、ActionParser
ActionParser定义于system/core/init/action.cpp中。
Action的解析过程,其实与Service一样,也是先后调用ParseSection, ParseLineSection和EndSection。

3.2.1、ParseSection

bool ActionParser::ParseSection(....) {
    //构造trigger
    std::vector<std::string> triggers(args.begin() + 1, args.end());
    if (triggers.size() < 1) {
        *err = "actions must have a trigger";
        return false;
    }

    //创建出新的action对象
    auto action = std::make_unique<Action>(false);
    //根据参数,填充action的trigger域,不详细分析了
    if (!action->InitTriggers(triggers, err)) {
        return false;
    }

    action_ = std::move(action);
    return true;
}

与Service类似,Action的ParseSection函数用于构造出Action对象。

3.2.2、ParseLineSection

bool ActionParser::ParseLineSection(.....) const {
    //构造Action对象的command域
    return action_ ? action_->AddCommand(args, filename, line, err) : false;
}

ParseLineSection将解析Action的子项,构造出Action对象的command域。

我们进一步看看AddCommand函数:

bool Action::AddCommand(const std::vector<std::string>& args,
                        const std::string& filename, int line, std::string* err) {
    ........
    //找出action对应的执行函数
    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
    ........
    //利用所有信息构造出command,加入到action对象中
    AddCommand(function, args, filename, line);
    return true;
}

void Action::AddCommand(......) {
    commands_.emplace_back(f, args, filename, line);
}

不难看出ParseLineSection主要是根据参数填充Action的command域。
一个Action对象中可以有多个command。

这里还剩下一个问题:Action中的function_map_是什么?
实际上,前文已经出现了过了,在init.cpp的main函数中:

.......
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
.......

因此,Action中调用function_map_->FindFunction时,
实际上调用的是BuiltinFunctionMap的FindFunction函数。

与查找Service一样,这里也是根据键值查找对应的信息,
因此重点是看看BuiltinFunctionMap的map函数。

在system/core/init/builtins.cpp中:

BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();

    static const Map builtin_functions = {
        {"bootchart",    {1,    1,    do_bootchart}},
        {"chmod",        {2,     2,    do_chmod}},
        ..................
    };
    return builtin_functions;
}

上述代码的第四项就是Action每个command对应的执行函数。

3.2.3、EndSection

void ActionParser::EndSection() {
    //Action有效时,才需要加入
    if (action_ && action_->NumCommands() > 0) {
        ActionManager::GetInstance().AddAction(std::move(action_));
    }
}

跟进AddAction函数:

void ActionManager::AddAction(.....) {
    //判断之前是否有定义过的Action
    //判断的依据是Action的trigger域
    auto auto old_action_it =
        std::find_if(actions_.begin(), actions_.end(),
                     [&action] (std::unique_ptr<Action>& a) {
                         return action->TriggersEqual(*a);
                     });

    //相对于Service,Action包容性强一些
    //重复定义时,会合并Action
    if (old_action_it != actions_.end()) {
        //主要是合并command
        (*old_action_it)->CombineAction(*action);
    } else {
        //加入到action链表中,类型也是vector,其中装的是指针
        actions_.emplace_back(std::move(action));
    }
}

从上面的代码可以看出,加载action块的逻辑和service一样,不同的是需要填充trigger和command域。
当然,最后解析出的action也需要加入到action链表中。

四、向Action队列中添加其它action
介绍完init进程解析init.rc文件的过程后,
我们继续将视角拉回到init进程的main函数:

...........
ActionManager& am = ActionManager::GetInstance();

am.QueueEventTrigger("early-init");

// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
m.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");

// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");

// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = property_get("ro.bootmode");
if (bootmode == "charger") {
    am.QueueEventTrigger("charger");
} else {
    am.QueueEventTrigger("late-init");
}

// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
......................

从上面的代码可以看出,init进程中调用了大量的QueueBuiltinAction和QueueEventTrigger函数。
接下来,我们就来看看这两个函数进行了哪些工作。

4.1、QueueBuiltinAction
QueueBuiltinAction的第一个参数作为新建action携带cmd的执行函数;
第二个参数既作为action的trigger name,也作为action携带cmd的参数。

void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
    //创建action
    auto action = std::make_unique<Action>(true);
    std::vector<std::string> name_vector{name};

    //保证trigger name的唯一性
    if (!action->InitSingleTrigger(name)) {
        return;
    }

    //创建action的cmd,指定执行函数和参数
    action->AddCommand(func, name_vector);

    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
    actions_.emplace_back(std::move(action));
}

从上面的代码可以看出:
QueueBuiltinAction函数中将构造新的action加入到actions_链表中,
并将trigger事件加入到trigger_queue_中。

4.2、QueueEventTrigger

void ActionManager::QueueEventTrigger(const std::string& trigger) {
    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
}

此处QueueEventTrigger函数就是利用参数构造EventTrigger,然后加入到trigger_queue_中。
后续init进程处理trigger事件时,将会触发相应的操作。

五、处理添加到运行队列的事件
前面的代码已经将Action、Service、Trigger等加入到数据结构中了。
最后就到了处理这些数据的时候了:

..............
while (true) {
    // By default, sleep until something happens.
    int epoll_timeout_ms = -1;

    //当前没有事件需要处理时
    if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
        //依次执行每个action中携带command对应的执行函数
        am.ExecuteOneCommand();
    }

    if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
        //重启一些挂掉的进程
        restart_processes();

        // If there's a process that needs restarting, wake up in time for that.
        if (process_needs_restart_at != 0) {
            决定timeout的时间,将影响while循环的间隔
            epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
            if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
        }

        // If there's more work to do, wake up again immediately.
        // 有command等着处理的话,不等待
        if (am.HasMoreCommands()) epoll_timeout_ms = 0;
    }

    epoll_event ev;
    //没有事件到来的话,最多阻塞epoll_timeout_ms时间
    int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
    if (nr == -1) {
        PLOG(ERROR) << "epoll_wait failed";
    } else if (nr == 1) {
        //有事件到来,执行对应处理函数
    //根据上文知道,epoll句柄(即epoll_fd)主要监听子进程结束,及其它进程设置系统属性的请求。
    ((void (*)()) ev.data.ptr)();
    }
}
.....................

从上面代码可以看出,最终init进程将进入无限循环中,
不断处理运行队列中的事件、完成重启进程、监听epoll_fd等操作。

接下来,我们关注一下其中比较关键的函数ExecuteOneCommand和restart_processes。

5.1、ExecuteOneCommand
ExecuteOneCommand中的主要部分如下图所示。

void ActionManager::ExecuteOneCommand() {
    // Loop through the trigger queue until we have an action to execute
    //当有可执行的action或trigger queue为空时结束
    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
        //轮询actions链表
        for (const auto& action : actions_) {
        //依次查找trigger表
            if (trigger_queue_.front()->CheckTriggers(*action)) {
                //当action与trigger对应时,就可以执行当前action
            //一个trigger可以对应多个action,均加入current_executing_actions_
                current_executing_actions_.emplace(action.get());
            }
        }
        //trigger event出队
        trigger_queue_.pop();
    }

    //上面的代码说明,执行的顺序又trigger queue决定

    //没有可执行的action时,直接退出
    if (current_executing_actions_.empty()) {
        return;
    }

    //每次只执行一个action,下次init进程while循环时,接着执行
    auto action = current_executing_actions_.front();

    if (current_command_ == 0) {
        std::string trigger_name = action->BuildTriggersString();
        INFO("processing action (%s)\n", trigger_name.c_str());
    }

    //实际的执行过程,此处仅处理当前action中的一个cmd
    //current_command_记录当前command对应的编号
    //实际上就是执行该command对应的处理函数
    action->ExecuteOneCommand(current_command_);

    //适当地清理工作,注意只有当前action中所有的command均执行完毕后,
    //才会将该action从current_executing_actions_移除
    // If this was the last command in the current action, then remove
    // the action from the executing list.
    // If this action was oneshot, then also remove it from actions_.
    ++current_command_;
    if (current_command_ == action->NumCommands()) {
        current_executing_actions_.pop();
        current_command_ = 0;
        if (action->oneshot()) {
            auto eraser = [&action] (std::unique_ptr<Action>& a) {
                return a.get() == action;
            };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
        }
    }
}

从代码可以看出,当while循环不断调用ExecuteOneCommand函数时,将按照trigger表的顺序,
依次取出action链表中与trigger匹配的action。

每次均仅仅执行一个action中的一个command对应函数(一个action可能携带多个command)。
当一个action所有的command均执行完毕后,再执行下一个action。
当一个trigger对应的action均执行完毕后,再执行下一个trigger对应action。

5.2、restart_processes
restart_processes函数负责按需重启进程,代码如下图所示:

static void restart_processes() {
    process_needs_restart = 0;
    ServiceManager::GetInstance().ForEachServiceWithFlags( SVC_RESTARTING, [] (Service* s) {
        s->RestartIfNeeded(process_needs_restart);
    });
}

从上面可以看出,该函数将轮询service对应的链表,
对于有SVC_RESTARING标志的service执行RestartIfNeeded函数。
前文已经提到过,当子进程终止时,init进程会将可被重启进程的服务标志位置为SVC_RESTARTING。

我们进一步看看RestartIfNeeded函数:

void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
    boot_clock::time_point now = boot_clock::now();
    boot_clock::time_point next_start = time_started_ + 5s;

    //两次服务启动进程的间隔要大于5s
    if (now > next_start) {
        flags_ &= (~SVC_RESTARTING);
        //满足时间间隔的要求后,重启进程
        //Start将会重新fork服务进程,并做相应的配置
        Start();
        return;
    }

    //更新process_needs_restart_at的值,将影响前文epoll_wait的等待时间
    time_t next_start_time_t = time(nullptr) +
        time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
    if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
        *process_needs_restart_at = next_start_time_t;
    }
}

六、结束
至此,init进程的启动流程分析完毕,
与Android 7.0相比,这部分流程的变化不是很大。

猜你喜欢

转载自blog.csdn.net/gaugamela/article/details/79291007