Android启动过程 - init.rc处理过程(二)

接上文

二、init进程执行rc文件

rc文件处理的大概表述:

将init.rc文件解析后,对于的执行,init进程主要通过以下几个角色实现:

  • Epoll(core/init/epoll.h):对<sys/epoll.h>的封装,用于事件的分发控制
  • ActionManager(core/init/action_manager.h):Action事件的队列管理、事件执行。
  • ServiceList(core/init/service_list.cpp):Service事件的队列管理、事件执行。
  • property_service(core/init/property_service.cpp):Property变化事件的管理

首先,我们回顾下,kernel在启动init进程后,依次调用core/init/init.cpp中的FirstStageMain、SetupSelinux后,会调用到SecondStageMain函数。SecondStageMain是解析执行init.rc文件的核心函数。

1. 关键角色的初始化

core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    ...
    // 创建epoll
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        PLOG(FATAL) << result.error();
    }
    
    ...
    // 初始化一个空的epoll事件,主要用来唤醒epoll
    InstallInitNotifier(&epoll);
    // 启动property服务,创建socket监听property变化,并通知回来进行trigger的匹配响应。
    StartPropertyService(&property_fd);    
    ...
    // 用来管理Action和Service的队列
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    // 解析rc文件
    LoadBootScripts(am, sm);
    
    // 开始将一系列所谓Buildin内建action加入队列
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
    // 将rc文件里early-init这个trigger所对应的Action加入队列
    am.QueueEventTrigger("early-init");
    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    Keychords keychords;
    am.QueueBuiltinAction(
            [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
                for (const auto& svc : ServiceList::GetInstance()) {
                    keychords.Register(svc->keycodes());
                }
                keychords.Start(&epoll, HandleKeychord);
                return {};
            },
            "KeychordInit");

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

上面的代码做了几个事情:

1)epoll.Open()

我们看下Epoll倒地是咋封装sys/epoll的:

Result<void> Epoll::Open() {
    if (epoll_fd_ >= 0) return {};
    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
    if (epoll_fd_ == -1) {
        return ErrnoError() << "epoll_create1 failed";
    }
    return {};
}

本质就是调用epoll_create1(EPOLL_CLOEXEC)创建了个epoll_fd_

2)InstallInitNotifier(&epoll);

core/init/init.cpp

// Init epolls various FDs to wait for various inputs.  It previously waited on property changes
// with a blocking socket that contained the information related to the change, however, it was easy
// to fill that socket and deadlock the system.  Now we use locks to handle the property changes
// directly in the property thread, however we still must wake the epoll to inform init that there
// is a change to process, so we use this FD.  It is non-blocking, since we do not care how many
// times WakeMainInitThread() is called, only that the epoll will wake.
static int wake_main_thread_fd = -1;
static void InstallInitNotifier(Epoll* epoll) {
    wake_main_thread_fd = eventfd(0, EFD_CLOEXEC);
    if (wake_main_thread_fd == -1) {
        PLOG(FATAL) << "Failed to create eventfd for waking init";
    }
    auto clear_eventfd = [] {
        uint64_t counter;
        TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter)));
    };

    if (auto result = epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd); !result.ok()) {
        LOG(FATAL) << result.error();
    }
}

注释部分理解为:原来在直接将监听property变化的socket注册进init的epoll,socket是阻塞的,而且该socket先于其他fd注册,因此很容易导致整个系统阻塞。所以改为:业务都在property线程里处理了,这里只是触发一个空的fd(wake_main_thread_fd)处理函数,只是用来唤醒epoll。

至于wake_main_thread_fd是在哪里写的:

// core/init/init.cpp
static void WakeMainInitThread() {
    uint64_t counter = 1;
    TEMP_FAILURE_RETRY(write(wake_main_thread_fd, &counter, sizeof(counter)));
}

WakeMainInitThread被掉用的地方在后面说明。

3)StartPropertyService(&property_fd);   

前面提到的property的socket,就是在这里创建的。看下具体代码:

core/init/property_service.cpp

void StartPropertyService(int* epoll_socket) {
    ...
    *epoll_socket = from_init_socket = sockets[0];
        if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                       /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
                                       /*gid=*/0, /*socketcon=*/{});
    listen(property_set_fd, 8);
    
    auto new_thread = std::thread{PropertyServiceThread};
    property_service_thread.swap(new_thread);
    ...
}

static void PropertyServiceThread() {
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        LOG(FATAL) << result.error();
    }

    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
    ...

即创建了PROP_SERVICE_NAME这个socket,监听property的设置,同时创建PropertyServiceThread线程,在该线程内单独创建了个Epoll来处理socket消息。这里的socket是消息的读取方, 而写入方,在/bionic/libc/bionic/system_property_set.cpp,也就是处理通过setprop设置property的地方。

我们再看看PropertyServiceThread内socket的处理函数handle_property_set_fd

static void handle_property_set_fd() {
    ...
    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    ...
    auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
    ...
}

最终会调用HandlePropertySet函数, 而这个HandlePropertySet在经历PropertySet -> NotifyPropertyChange 的调用后,会调用core/init/init.cpp内的PropertyChanged

core/init/init.cpp

void PropertyChanged(const std::string& name, const std::string& value) {
    ...
    if (property_triggers_enabled) {
        ActionManager::GetInstance().QueuePropertyChange(name, value);
        WakeMainInitThread();
    }
    prop_waiter_state.CheckAndResetWait(name, value);
}

我们看到,显示ActionManager::GetInstance().QueuePropertyChange(name, value);将property变更消息加入队列,然后调用WakeMainInitThread唤醒init的epoll。

2. 事件加入队列

1)将rc文件内的事件加入队列

init.cpp中SecondStageMain函数 -> LoadBootScripts()触发了对rc文件的解析

int SecondStageMain(int argc, char** argv) {
    ...
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    LoadBootScripts(am, sm);
    ...
}

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);
    ...
}
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;

    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    return parser;
}

我们知道,rc文件中,主要有两类事件,即所谓的可被执行的Section -- ActionService

在上面代码中分别对Action和Service的解析器ActionParserServiceParser做了初始化

这两个解析器在解析出“on”语法的Action和“service”语法的Service后,会构造响应的两个对象,然后分别加入到传进来的service_list和action_manager, 也就是ServiceList::GetInstance()ActionManager::GetInstance()

首先看下Action和Service的构造函数:

core/init/action.capp

Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
               const std::string& event_trigger,
               const std::map<std::string, std::string>& property_triggers)
    : property_triggers_(property_triggers),
      event_trigger_(event_trigger),
      oneshot_(oneshot),
      subcontext_(subcontext),
      filename_(filename),
      line_(line) {}

core/init/service.cpp

Service::Service(const std::string& name, unsigned flags, std::optional<uid_t> uid, gid_t gid,
                 const std::vector<gid_t>& supp_gids, int namespace_flags,
                 const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
                 const std::string& filename, const std::vector<std::string>& args)
    : name_(name),
      classnames_({"default"}),
      flags_(flags),
      pid_(0),
      crash_count_(0),
      proc_attr_{.ioprio_class = IoSchedClass_NONE,
                 .ioprio_pri = 0,
                 .parsed_uid = uid,
                 .gid = gid,
                 .supp_gids = supp_gids,
                 .priority = 0},
      namespaces_{.flags = namespace_flags},
      seclabel_(seclabel),
      subcontext_(subcontext_for_restart_commands),
      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
                 "onrestart", {}),
      oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
      start_order_(0),
      args_(args),
      filename_(filename) {}

我们看看这两个事件分别怎么放入队列等待执行的。

对Action的解析在core/init/action_parser.cpp

core/init/action_parser.cpp

Result<void> ActionParser::ParseSection(std::vector<std::string>&& args,const std::string& filename, int line) {
    ...
    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
                                           property_triggers);
    action_ = std::move(action); 
}

Result<void> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
    return action_ ? action_->AddCommand(std::move(args), line) : Result<void>{};
}

Result<void> ActionParser::EndSection() {
    action_manager_->AddAction(std::move(action_));
}

首先,在ParseSection中构建了Action,随后在ParseLineSection中将每行的command通过AddCommand加入到action内部的command_队列等待执行。后调用ActionManagerAddAction将action加入到ActionManager的队列中。

core/init/action_manager.cpp

void ActionManager::AddAction(std::unique_ptr<Action> action) {
    actions_.emplace_back(std::move(action));
}

放入ActionManageractions_

另外,我们有必要看下AddCommand的细节:

core/init/action.cpp

Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
    if (!function_map_) {
        return Error() << "no function map available";
    }

    auto map_result = function_map_->Find(args);
    if (!map_result.ok()) {
        return Error() << map_result.error();
    }

    commands_.emplace_back(map_result->function, map_result->run_in_subcontext, std::move(args),
                           line);
    return {};
}

function_map_是一个内置的指令表,事先声明了所有comand对应的函数:

// Builtin-function-map start
const BuiltinFunctionMap& GetBuiltinFunctionMap() {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    static const BuiltinFunctionMap builtin_functions = {
        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
        {"chmod",                   {2,     2,    {true,   do_chmod}}},
        {"chown",                   {2,     3,    {true,   do_chown}}},
        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
        {"class_restart",           {1,     2,    {false,  do_class_restart}}},
        {"class_start",             {1,     1,    {false,  do_class_start}}},
        {"class_stop",              {1,     1,    {false,  do_class_stop}}},
        {"copy",                    {2,     2,    {true,   do_copy}}},
        {"copy_per_line",           {2,     2,    {true,   do_copy_per_line}}},
        {"domainname",              {1,     1,    {true,   do_domainname}}},
        {"enable",                  {1,     1,    {false,  do_enable}}},
        {"exec",                    {1,     kMax, {false,  do_exec}}},
        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
        {"exec_start",              {1,     1,    {false,  do_exec_start}}},
        {"export",                  {2,     2,    {false,  do_export}}},
        {"hostname",                {1,     1,    {true,   do_hostname}}},
        {"ifup",                    {1,     1,    {true,   do_ifup}}},
        {"init_user0",              {0,     0,    {false,  do_init_user0}}},
        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
        {"installkey",              {1,     1,    {false,  do_installkey}}},
        {"interface_restart",       {1,     1,    {false,  do_interface_restart}}},
        {"interface_start",         {1,     1,    {false,  do_interface_start}}},
        {"interface_stop",          {1,     1,    {false,  do_interface_stop}}},
        {"load_exports",            {1,     1,    {false,  do_load_exports}}},
        {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
        {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
        {"loglevel",                {1,     1,    {false,  do_loglevel}}},
        {"mark_post_data",          {0,     0,    {false,  do_mark_post_data}}},
        {"mkdir",                   {1,     6,    {true,   do_mkdir}}},
        // TODO: Do mount operations in vendor_init.
        // mount_all is currently too complex to run in vendor_init as it queues action triggers,
        // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
        // mount and umount are run in the same context as mount_all for symmetry.
        {"mount_all",               {0,     kMax, {false,  do_mount_all}}},
        {"mount",                   {3,     kMax, {false,  do_mount}}},
        {"perform_apex_config",     {0,     1,    {false,  do_perform_apex_config}}},
        {"umount",                  {1,     1,    {false,  do_umount}}},
        {"umount_all",              {0,     1,    {false,  do_umount_all}}},
        {"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},
        {"readahead",               {1,     2,    {true,   do_readahead}}},
        {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
        {"restart",                 {1,     2,    {false,  do_restart}}},
        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
        {"rm",                      {1,     1,    {true,   do_rm}}},
        {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
        {"setprop",                 {2,     2,    {true,   do_setprop}}},
        {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
        {"start",                   {1,     1,    {false,  do_start}}},
        {"stop",                    {1,     1,    {false,  do_stop}}},
        {"swapon_all",              {0,     1,    {false,  do_swapon_all}}},
        {"enter_default_mount_ns",  {0,     0,    {false,  do_enter_default_mount_ns}}},
        {"symlink",                 {2,     2,    {true,   do_symlink}}},
        {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
        {"trigger",                 {1,     1,    {false,  do_trigger}}},
        {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
        {"wait",                    {1,     2,    {true,   do_wait}}},
        {"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},
        {"write",                   {2,     2,    {true,   do_write}}},
    };
    // clang-format on
    return builtin_functions;
}

也就是说,AddCommand函数里,通过rc里面声明的指令,从GetBuiltinFunctionMap里取出对应的函数,加入到action.cpp的commands_中。

再来看看Service:

core\init\service_parser.cpp

Result<void> ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename, int line) {
     ...
     ervice_ = std::make_unique<Service>(name, restart_action_subcontext, filename, str_args);
 }
 
 Result<void> ServiceParser::EndSection() {
     ...
     service_list_->AddService(std::move(service_));
 }

构建了Service后,加入到service_list_,也就是ServiceList::GetInstance()

2)代码内直接添加Action到队列

Action事件除了在rc里面声明,还可以在代码里直接调用。

SecondStageMain函数中,在经过上面的初始化后,我们看到其后有下面这些调用:

am.QueueBuiltinAction(...    : 构建Buildin(内建)Action,也就是代码方式直接将Action对象加入队列执行。

am.QueueEventTrigger(... :将trigger加入队列,实际上是触发rc文件里的某个匹配trigger的Action执行。

加上上面提到的QueuePropertyChange:触发property trigger的执行。

看下ActionManager类里面的实现:

void ActionManager::QueueEventTrigger(const std::string& trigger) {
    auto lock = std::lock_guard{event_queue_lock_};
    event_queue_.emplace(trigger);
}

QueueEventTrigger是把trigger加入到event_queue_队列;

void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
    auto lock = std::lock_guard{event_queue_lock_};
    event_queue_.emplace(std::make_pair(name, value));
}

QueuePropertyChange是把name-value构建pair后加入event_queue_队列

void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
    auto lock = std::lock_guard{event_queue_lock_};
    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,std::map<std::string, std::string>{});
    action->AddCommand(std::move(func), {name}, 0);

    event_queue_.emplace(action.get());
    actions_.emplace_back(std::move(action));
}

QueueBuiltinAction中,新建了个Action,然后把func参数当做一个command加入到action。随后将action加入event_queue_队列,同时加入到actions_维护。

我们看看core/init/action_manager.h中event_queue_的声明:

std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_

可以看到队列接受EventTrigger, PropertyChange, BuiltinAction这三个类型,关于类型声明在core/init/action.h中:

using EventTrigger = std::string;
using PropertyChange = std::pair<std::string, std::string>;
using BuiltinAction = class Action*;

3. 事件的执行

SecondStageMain函数的最后,有一个while死循环:

core/init/init.cpp

while (true) {
    // Action的执行
    if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
            if (am.HasMoreCommands()) {
                next_action_time = boot_clock::now();
            }
    }
    
    // 计算Service的超时、重启
    if (!IsShuttingDown()) {
            auto next_process_action_time = HandleProcessActions();
    }
    ...
    // 计算下次执行事件
    if (next_action_time != far_future) {
            epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                    std::max(next_action_time - boot_clock::now(), 0ns));
        }
    auto epoll_result = epoll.Wait(epoll_timeout);
    ...    
}

会调用am.ExecuteOneCommand(); 执行Action。每执行完一次,会在epoll.Wait(epoll_timeout);阻塞,等待下一次唤醒。

看看am.ExecuteOneCommand

core/init/action_manager.cpp

void ActionManager::ExecuteOneCommand() {
    while (current_executing_actions_.empty() && !event_queue_.empty()) {
            for (const auto& action : actions_) {
                if (std::visit([&action](const auto& event) { return action->CheckEvent(event); }, event_queue_.front())) {
                current_executing_actions_.emplace(action.get());
                }
            }
            event_queue_.pop();
        }
    ...
    auto action = current_executing_actions_.front();
    action->ExecuteOneCommand(current_command_);
}

即从actions_队列中取出每个Action,最终执行action的ExecuteOneCommand。

Service的执行,则依赖于Action的start命令。例如rc中:

on zygote-start
    ...
    start zygote

这里的start zygote即启动之前声明好的service zygote服务。对于start命令,其对应的执行函数在

GetBuiltinFunctionMap()中声明:

core/init/builtins.cpp

// Builtin-function-map start
const BuiltinFunctionMap& GetBuiltinFunctionMap() {

    static const BuiltinFunctionMap builtin_functions = {
        ...
        {"start",                   {1,     1,    {false,  do_start}}},
        ...
    }
}

do_start函数内容:

static Result<void> do_start(const BuiltinArguments& args) {
    Service* svc = ServiceList::GetInstance().FindService(args[1]);
    if (!svc) return Error() << "service " << args[1] << " not found";
    errno = 0;
    if (auto result = svc->Start(); !result.ok()) {
        return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
    }
    return {};
}

可以看到实际从ServiceList::GetInstance()中取出对应Servie,调用其Start()函数执行。执行了Service的Start函数

core/init/service.cpp

Result<void> Service::Start() {
    ...
    pid_t pid = -1;
    if (namespaces_.flags) {
        pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
    } else {
        pid = fork();
    }

    if (pid == 0) {
        umask(077);
        cgroups_activated.CloseWriteFd();
        setsid_finished.CloseReadFd();
        RunService(descriptors, std::move(cgroups_activated), std::move(setsid_finished));
        _exit(127);
    } else {
        cgroups_activated.CloseReadFd();
        setsid_finished.CloseWriteFd();
    }
}

根据是否设置了namespaces_.flags(在options中指定)来选择用clone还是fork创建一个新进程。随后在新进程中执行RunService函数执行具体Service内容

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值