Androidシステム起動のプロファイル解析
22493 ワード
以下のコードはAndroid 7.0分析に基づく
概要
Androidシステムが起動するとInitプロセスが作成され、Initプロセスのmain()エントリ関数でシステムプロファイルが解析されてサービスプロセスの作成と起動が行われることがわかります.
解析rcプロファイル
[->system/core/init/init.cpp]
解析中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):
もう1つのセクションは、「サービス」キーで始まるサービスリストです.
システム環境変数またはLinuxコマンドを使用して、アクションリストは必要なディレクトリを作成し、特定のファイルに権限を指定します.サービスリストは、initプロセスが起動する必要がある一連の列のサブプロセスを記録します.上記のコードに示すように、サービスキーワードの後の最初の文字列はサービス(サブプロセス)の名前を表し、2番目の文字列はサービスの実行パスを表す.次に,ParseConfig関数から着手し,解析過程全体を逐次解析した.[->system/core/init/init_parser.cpp]
ここでの解析は複雑に見えますが、6.0以前のバージョンでは、解析全体がプロセス向けです.Initプロセスは、解析のために関数を統一的に呼び出し、その関数でswitch-caseの形式を利用して、解析の内容に応じて対応する処理を行います.Android 7.0では、オブジェクトをよりよくカプセル化し、オブジェクトに向かうために、異なるキーワードに対して異なるparserオブジェクトを定義し、各オブジェクトはマルチステートによって独自の解析操作を実現する.次にinitプロセスmain関数で作成した3つのparseコードを往復します
3つのParseの定義を見てみましょう
3つのParserはいずれもSectionParserを継承していることがわかりますが、具体的な実装はそれぞれ異なります.一般的なServiceParserとActionParserを比較して、解析の結果がどのように処理されるかを見てみましょう.
ServiceParser
ServiceParserはシステム/core/init/serviceで定義する.cpp中.前のコードから、サービスブロックを解析するには、まずParseSection関数を呼び出す必要があります.次に、ParseLineSectionでサブブロックを処理し、データを解析した後、EndSectionを呼び出す必要があります.そこで,この3つの関数に重点を置いてみる.[->system/core/init/service.cpp]
上記の一連のコードから、ServiceParserは、まず最初の行の名前とパラメータに基づいてサービスオブジェクトを作成し、オプションドメインの内容に基づいてサービスオブジェクトを埋め込み、最後に作成したサービスオブジェクトをvectorタイプのサービスチェーンテーブルに追加することがわかります.
ActionParser
ActionParserはシステム/core/init/actionに定義する.cpp中.Actionの解析プロセスは,サービスと同様にParseSection,ParseLineSection,EndSectionを前後して呼び出す.[->system/core/init/action.cpp]
上記のコードから,actionブロックをロードする論理はserviceと同様にtriggerとcommandドメインを埋め込む必要があることがわかる.もちろん,最後に解析したactionもactionチェーンテーブルに加わる必要がある.ここで最後に残る問題は,Actionにおけるcommand対応処理関数をどこで定義したかということである.実は、前文はすでに現れています.init.cppのmain関数では:
したがって、Actionでfunction_が呼び出されるmap->FindFunctionの場合、実際にはBuiltinFunctionMapのFindFunction関数が呼び出されます.FindFunctionはkeyword定義の汎用関数であり,再構成map関数に重点を置いていることが分かった.システム/core/init/builtinsを見てみましょう.cpp:
上のコードの各項目の最後の項目は、Actionの各commandに対応する実行関数です.
実行キューに追加アクションを追加
Initプロセス解析initを紹介します.rcファイルのプロセス後、initプロセスのmain関数に視点を戻し続けます.
上のコードから分かるように、次のinitプロセスでは、QueueEventTriggerとQueueBuiltinAction関数が大量に呼び出されます.
ここでQueueEventTrigger関数は、パラメータを使用してEventTriggerを構築し、trigger_に追加します.queue_に表示されます.後続のinitプロセスがtriggerイベントを処理すると、対応するアクションがトリガーされます.前文の分析によると、実際にはaction_Listでは、triggerが最初のパラメータと一致するactionに対応して、実行キューaction_に追加されます.を選択します.
QueueBuiltinAction関数に新しいactionを作成してactions_に追加で、最初のパラメータは、新しいactionとしてcmdを携帯する実行関数である.2番目のパラメータはactionのtrigger nameとしても、actionがcmdを運ぶパラメータとしても使用されます.main関数のメインフローを続行
上記のコードから、initプロセスは、操作が必要なすべてのactionを実行キューに追加した後、無限ループプロセスに入り、実行キュー内のイベントを処理しながらサービスの再起動などの操作を行うことがわかります.
ExecuteOneCommandの主な部分を下図に示します.
コードから分かるように,whileループがExecuteOneCommand関数を呼び出し続けると,triggerテーブルの順にactionチェーンテーブルからtriggerに一致するactionが順次取り出される.毎回、1つのactionの1つのcommand対応関数が実行されます(1つのactionは複数のcommandを運ぶ可能性があります).1つのactionのすべてのcommandが実行されたら、次のactionを実行します.1つのtriggerに対応するactionがすべて実行されると、次のtriggerに対応するactionが実行されます.
restart_プロセス関数は、サービスをオンデマンドで再起動します.コードは次のとおりです.
以上から分かるように、この関数はサービスに対応するチェーンテーブルをポーリングし、SVC_RESTARINGフラグのサービスはRestartIfNeededを実行する(前述のように、サブプロセスが終了すると、initプロセスは再起動可能なプロセスのサービスフラグ位置をSVC_RESTARTINGとする).次のコードに示すrestart_service_if_neededはサービスを再起動できます.
まとめ
プロファイルの解析についてAndroid 6.0と7.0全体の流れはほとんど変わらないが,7.0はより良いパッケージ性のために解析ファイル後の記憶用のデータ構造を修正し,同時にいくつかの多態関連コンテンツを導入した.人は後ろを見るが、6.0にはkeywordsのような面白いところがたくさんある.hのマクロ置換やlistnodeを用いて汎用的なチェーンテーブルなどを組織する.この部分では、「Android巻Iを深く理解する」を参照してください.本の対応コードは古いですが、大体同じ考えです.
概要
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を深く理解する」を参照してください.本の対応コードは古いですが、大体同じ考えです.