Androidシステム起動のプロファイル解析

22493 ワード

以下のコードはAndroid 7.0分析に基づく
概要
Androidシステムが起動するとInitプロセスが作成され、Initプロセスのmain()エントリ関数でシステムプロファイルが解析されてサービスプロセスの作成と起動が行われることがわかります.
解析rcプロファイル
[->system/core/init/init.cpp]
int main(int argc, char** argv) {
  ...
  //   Action function_map_   BuiltinFunctionMap
  //     BuiltinFuntionMap map  ,  keyword       
  const BuiltinFunctionMap function_map;
  Action::set_function_map(&function_map);

  //         parser  
  Parser& parser = Parser::GetInstance();
  //         ,     parser
  parser.AddSectionParser("service",std::make_unique());
  parser.AddSectionParser("on", std::make_unique());
  parser.AddSectionParser("import", std::make_unique());
  //         
  parser.ParseConfig("/init.rc");
  ...
}

解析中rcファイルの前に、initを簡単に紹介します.rcファイル.init.rcファイルはinitプロセスの起動後に実行される起動スクリプトで、initプロセスが実行する操作が記録されています.Androidシステムではinitを使用する.rcとinit.{ hardware }.rcの2つのファイル.
そのうちrcファイルはAndroidシステムの実行中に共通の環境設定に使用するプロセスに関する定義、init.{hardware}.rc(例えば、高通にはinit.qcom.rc、MTKにはinit.mediatek.rc)は、Androidの異なるプラットフォームでの特定のプロセスや環境設定などを定義するために使用されます.
ここのinit.rcはsystem/core/rootdir/initに位置する.rc中です.init.rcファイルは大きく2つの部分に分けられ、一部は「on」キーで始まるアクションリスト(action list):
on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000
    .........
    start ueventd

もう1つのセクションは、「サービス」キーで始まるサービスリストです.
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

システム環境変数またはLinuxコマンドを使用して、アクションリストは必要なディレクトリを作成し、特定のファイルに権限を指定します.サービスリストは、initプロセスが起動する必要がある一連の列のサブプロセスを記録します.上記のコードに示すように、サービスキーワードの後の最初の文字列はサービス(サブプロセス)の名前を表し、2番目の文字列はサービスの実行パスを表す.次に,ParseConfig関数から着手し,解析過程全体を逐次解析した.[->system/core/init/init_parser.cpp]
bool Parser::ParseConfig(const std::string& path) {
    //path /init.rc
    if (is_dir(path.c_str())) {
        //         
        return ParseConfigDir(path);
    }
    //         
    return ParseConfigFile(path);
}

bool Parser::ParseConfigDir(const std::string& path) {
    ...........
    std::unique_ptr config_dir(opendir(path.c_str()), closedir);
    ..........
    //      ,        
    while ((current_file = readdir(config_dir.get()))) {
        std::string current_path = android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
        if (current_file->d_type == DT_REG) {
            //     ParseConfigFile        
            if (!ParseConfigFile(current_path)) {
                .............
            }
        }
    }
}

bool Parser::ParseConfigFile(const std::string& path) {
    ........
    std::string data;
    //            ,        
    if (!read_file(path.c_str(), &data)) {
        return false;
    }
    .........
    //        
    ParseData(path, data);
    .........
}

void Parser::ParseData(const std::string& filename, const std::string& data) {
    .......
    parse_state state;
    .......
    std::vector<: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
", ret_err.c_str()); section_parser = nullptr; } } else if (section_parser) { std::string ret_err; // service,on,import // parser ParseLineSection // if (!section_parser->ParseLineSection(args, state.filename, state.line, &ret_err)) { parse_error(&state, "%s
", ret_err.c_str()); } } // args.clear(); break; case T_TEXT: // args args.emplace_back(state.text); break; } } }

ここでの解析は複雑に見えますが、6.0以前のバージョンでは、解析全体がプロセス向けです.Initプロセスは、解析のために関数を統一的に呼び出し、その関数でswitch-caseの形式を利用して、解析の内容に応じて対応する処理を行います.Android 7.0では、オブジェクトをよりよくカプセル化し、オブジェクトに向かうために、異なるキーワードに対して異なるparserオブジェクトを定義し、各オブジェクトはマルチステートによって独自の解析操作を実現する.次にinitプロセスmain関数で作成した3つのparseコードを往復します
    Parser& parser = Parser::GetInstance();
    parser.AddSectionParser("service",std::make_unique());
    parser.AddSectionParser("on", std::make_unique());
    parser.AddSectionParser("import", std::make_unique());

3つのParseの定義を見てみましょう
class ServiceParser : public SectionParser {......}
class ActionParser : public SectionParser {......}
class ImportParser : public SectionParser {.......}

3つのParserはいずれもSectionParserを継承していることがわかりますが、具体的な実装はそれぞれ異なります.一般的なServiceParserとActionParserを比較して、解析の結果がどのように処理されるかを見てみましょう.
ServiceParser
ServiceParserはシステム/core/init/serviceで定義する.cpp中.前のコードから、サービスブロックを解析するには、まずParseSection関数を呼び出す必要があります.次に、ParseLineSectionでサブブロックを処理し、データを解析した後、EndSectionを呼び出す必要があります.そこで,この3つの関数に重点を置いてみる.[->system/core/init/service.cpp]
//         service  
bool ServiceParser::ParseSection(.....) {
    .......
    const std::string& name = args[1];
    .......
    std::vector<:string> str_args(args.begin() + 2, args.end());
    //      ,     service  
    service_ = std::make_unique(name, "default", str_args);
    return true;
}

//            
bool ServiceParser::ParseLineSection(......) const {
    //  service   HandleLine
    return service_ ? service_->HandleLine(args, err) : false;
}

bool Service::HandleLine(.....) {
    ........
    //OptionHandlerMap   keywordMap
    static const OptionHandlerMap handler_map;
    //       ,     handler  
    //FindFunction   keyword   ,FindFunction           map ,           ,              
    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);

    if (!handler) {
        return false;
    }
    //  handler  
    return (this->*handler)(args, err);
}

class Service::OptionHandlerMap : public KeywordMap {
    ...........
    Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<:size_t>::max();
    static const Map option_handlers = {
        {"class",       {1,     1,    &Service::HandleClass}},
        {"console",     {0,     0,    &Service::HandleConsole}},
        {"critical",    {0,     0,    &Service::HandleCritical}},
        {"disabled",    {0,     0,    &Service::HandleDisabled}},
        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
        {"ioprio",      {2,     2,    &Service::HandleIoprio}},
        {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
        {"oneshot",     {0,     0,    &Service::HandleOneshot}},
        {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
        {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
        {"setenv",      {2,     2,    &Service::HandleSetenv}},
        {"socket",      {3,     6,    &Service::HandleSocket}},
        {"user",        {1,     1,    &Service::HandleUser}},
        {"writepid",    {1,     kMax, &Service::HandleWritepid}},
    };
    return option_handlers;
}

// class         ,          service      
bool Service::HandleClass(const std::vector<:string>& args, std::string* err) {
    classname_ = args[1];
    return true;
}

//    service        
void ServiceParser::EndSection() {
    if (service_) {
        ServiceManager::GetInstance().AddService(std::move(service_));
    }
}

void ServiceManager::AddService(std::unique_ptr service) {
    Service* old_service = FindServiceByName(service->name());
    if (old_service) {
        ERROR("ignored duplicate definition of service '%s'",
              service->name().c_str());
        return;
    }
    // service     services_ 
    //7.0 ,services_    vector 
    services_.emplace_back(std::move(service));
}

上記の一連のコードから、ServiceParserは、まず最初の行の名前とパラメータに基づいてサービスオブジェクトを作成し、オプションドメインの内容に基づいてサービスオブジェクトを埋め込み、最後に作成したサービスオブジェクトをvectorタイプのサービスチェーンテーブルに追加することがわかります.
ActionParser
ActionParserはシステム/core/init/actionに定義する.cpp中.Actionの解析プロセスは,サービスと同様にParseSection,ParseLineSection,EndSectionを前後して呼び出す.[->system/core/init/action.cpp]
bool ActionParser::ParseSection(....) {
    ........
    //     action  
    auto action = std::make_unique(false);
    //    ,  action trigger ,      
    if (!action->InitTriggers(triggers, err)) {
        return false;
    }
    .........
}

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

bool Action::AddCommand(.....) {
    ........
    //  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);
}

void ActionParser::EndSection() {
    if (action_ && action_->NumCommands() > 0) {
        ActionManager::GetInstance().AddAction(std::move(action_));
    }
}

void ActionManager::AddAction(.....) {
    ........
    auto old_action_it = std::find_if(actions_.begin(),
                     actions_.end(),
                     [&action] (std::unique_ptr& a) {
                         return action->TriggersEqual(*a);
                     });

    if (old_action_it != actions_.end()) {
        (*old_action_it)->CombineAction(*action);
    } else {
        //   action   ,    vector,       
        actions_.emplace_back(std::move(action));
    }
}

上記のコードから,actionブロックをロードする論理はserviceと同様にtriggerとcommandドメインを埋め込む必要があることがわかる.もちろん,最後に解析したactionもactionチェーンテーブルに加わる必要がある.ここで最後に残る問題は,Actionにおけるcommand対応処理関数をどこで定義したかということである.実は、前文はすでに現れています.init.cppのmain関数では:
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);

したがって、Actionでfunction_が呼び出されるmap->FindFunctionの場合、実際にはBuiltinFunctionMapのFindFunction関数が呼び出されます.FindFunctionはkeyword定義の汎用関数であり,再構成map関数に重点を置いていることが分かった.システム/core/init/builtinsを見てみましょう.cpp:
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<:size_t>::max();
    static const Map builtin_functions = {
        {"bootchart_init",          {0,     0,    do_bootchart_init}},
        {"chmod",                   {2,     2,    do_chmod}},
        {"chown",                   {2,     3,    do_chown}},
        {"class_reset",             {1,     1,    do_class_reset}},
        {"class_start",             {1,     1,    do_class_start}},
        {"class_stop",              {1,     1,    do_class_stop}},
        {"copy",                    {2,     2,    do_copy}},
        {"domainname",              {1,     1,    do_domainname}},
        {"enable",                  {1,     1,    do_enable}},
        {"exec",                    {1,     kMax, do_exec}},
        {"export",                  {2,     2,    do_export}},
        {"hostname",                {1,     1,    do_hostname}},
        {"ifup",                    {1,     1,    do_ifup}},
        {"init_user0",              {0,     0,    do_init_user0}},
        {"insmod",                  {1,     kMax, do_insmod}},
        {"installkey",              {1,     1,    do_installkey}},
        {"load_persist_props",      {0,     0,    do_load_persist_props}},
        {"load_system_props",       {0,     0,    do_load_system_props}},
        {"loglevel",                {1,     1,    do_loglevel}},
        {"mkdir",                   {1,     4,    do_mkdir}},
        {"mount_all",               {1,     kMax, do_mount_all}},
        {"mount",                   {3,     kMax, do_mount}},
        {"powerctl",                {1,     1,    do_powerctl}},
        {"restart",                 {1,     1,    do_restart}},
        {"restorecon",              {1,     kMax, do_restorecon}},
        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
        {"rm",                      {1,     1,    do_rm}},
        {"rmdir",                   {1,     1,    do_rmdir}},
        {"setprop",                 {2,     2,    do_setprop}},
        {"setrlimit",               {3,     3,    do_setrlimit}},
        {"start",                   {1,     1,    do_start}},
        {"stop",                    {1,     1,    do_stop}},
        {"swapon_all",              {1,     1,    do_swapon_all}},
        {"symlink",                 {2,     2,    do_symlink}},
        {"sysclktz",                {1,     1,    do_sysclktz}},
        {"trigger",                 {1,     1,    do_trigger}},
        {"verity_load_state",       {0,     0,    do_verity_load_state}},
        {"verity_update_state",     {0,     0,    do_verity_update_state}},
        {"wait",                    {1,     2,    do_wait}},
        {"write",                   {2,     2,    do_write}},
    };
    return builtin_functions;
}

上のコードの各項目の最後の項目は、Actionの各commandに対応する実行関数です.
実行キューに追加アクションを追加
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(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プロセスでは、QueueEventTriggerとQueueBuiltinAction関数が大量に呼び出されます.
void ActionManager::QueueEventTrigger(const std::string& trigger) {
    trigger_queue_.push(std::make_unique(trigger));
}

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

    //     
    if (!action->InitSingleTrigger(name)) {
        return;
    }

    //  action cmd,         
    action->AddCommand(func, name_vector);

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

ここでQueueEventTrigger関数は、パラメータを使用してEventTriggerを構築し、trigger_に追加します.queue_に表示されます.後続のinitプロセスがtriggerイベントを処理すると、対応するアクションがトリガーされます.前文の分析によると、実際にはaction_Listでは、triggerが最初のパラメータと一致するactionに対応して、実行キューaction_に追加されます.を選択します.
QueueBuiltinAction関数に新しいactionを作成してactions_に追加で、最初のパラメータは、新しいactionとしてcmdを携帯する実行関数である.2番目のパラメータはactionのtrigger nameとしても、actionがcmdを運ぶパラメータとしても使用されます.main関数のメインフローを続行
while (true) {
    //           
    if (!waiting_for_exec) {
        //      action   command       
        am.ExecuteOneCommand();
        //         
        restart_processes();
    }

    //    timeout   ,   while     
    int timeout = -1;
    //        ,       
    if (process_needs_restart) {
        timeout = (process_needs_restart - gettime()) * 1000;
        if (timeout < 0)
            timeout = 0;
    }

    // action   ,   
    if (am.HasMoreCommands()) {
        timeout = 0;
    }

    //bootchart_sample           
    bootchart_sample(&timeout);

    epoll_event ev;
    //        ,    timeout  
    int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
    if (nr == -1) {
        ERROR("epoll_wait failed: %s
", strerror(errno)); } else if (nr == 1) { // , // ,epoll ( epoll_fd) , 。 ((void (*)()) ev.data.ptr)(); } }

上記のコードから、initプロセスは、操作が必要なすべてのactionを実行キューに追加した後、無限ループプロセスに入り、実行キュー内のイベントを処理しながらサービスの再起動などの操作を行うことがわかります.
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();
    }

    if (current_executing_actions_.empty()) {
        return;
    }

    //       action,  init  while   ,     while  ,    
    auto action = current_executing_actions_.front();

    if (current_command_ == 0) {
        std::string trigger_name = action->BuildTriggersString();
        INFO("processing action (%s)
", trigger_name.c_str()); } // , action cmd 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& a) { return a.get() == action; }; actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser)); } } } void Action::ExecuteCommand(const Command& command) const { Timer t; // command int result = command.InvokeFunc(); ........ }

コードから分かるように,whileループがExecuteOneCommand関数を呼び出し続けると,triggerテーブルの順にactionチェーンテーブルからtriggerに一致するactionが順次取り出される.毎回、1つのactionの1つのcommand対応関数が実行されます(1つのactionは複数のcommandを運ぶ可能性があります).1つのactionのすべてのcommandが実行されたら、次のactionを実行します.1つのtriggerに対応するactionがすべて実行されると、次のtriggerに対応するactionが実行されます.
restart_プロセス関数は、サービスをオンデマンドで再起動します.コードは次のとおりです.
static void restart_processes() {
    process_needs_restart = 0;
    ServiceManager::GetInstance().ForEachServiceWithFlags(
        SVC_RESTARTING,
        [] (Service* s) {
            s->RestartIfNeeded(process_needs_restart);
        });
}

以上から分かるように、この関数はサービスに対応するチェーンテーブルをポーリングし、SVC_RESTARINGフラグのサービスはRestartIfNeededを実行する(前述のように、サブプロセスが終了すると、initプロセスは再起動可能なプロセスのサービスフラグ位置をSVC_RESTARTINGとする).次のコードに示すrestart_service_if_neededはサービスを再起動できます.
void Service::RestartIfNeeded(time_t& process_needs_restart)(struct service *svc)
{
    time_t next_start_time = svc->time_started + 5;

    //              5s
    if (next_start_time <= gettime()) {
        svc->flags &= (~SVC_RESTARTING);
        //          ,    
        //Start    fork    ,       
        Start(svc, NULL);
        return;
    }

    //  main   ,while         
    if ((next_start_time < process_needs_restart) ||
        (process_needs_restart == 0)) {
        process_needs_restart = next_start_time;
    }
}

まとめ
プロファイルの解析についてAndroid 6.0と7.0全体の流れはほとんど変わらないが,7.0はより良いパッケージ性のために解析ファイル後の記憶用のデータ構造を修正し,同時にいくつかの多態関連コンテンツを導入した.人は後ろを見るが、6.0にはkeywordsのような面白いところがたくさんある.hのマクロ置換やlistnodeを用いて汎用的なチェーンテーブルなどを組織する.この部分では、「Android巻Iを深く理解する」を参照してください.本の対応コードは古いですが、大体同じ考えです.