Androidのinitプロセスの詳細(三)
107705 ワード
解析サービス(1)
1.parse_service
解析サービス先parse_サービスが開始します.コードは次のとおりです.
parse_サービス関数は主に3つの作業を行った:1)新しいサービスにストレージスペースを割り当て,2)サービスを初期化し,
3)サービスを1つのサービスに入れる_Listチェーンテーブル.重要なデータ型と関数はいくつかあります.service_list、list_initとlist_add_tail、およびservice構造体.
(1)service_list
service_リストはlist_からdeclare定義、list_declareは実際には/system/core/include/cutils/listにあるマクロです.h.ソースコードは次のとおりです.
service_Listは、前方ポインタと後方ポインタを格納する双方向チェーンテーブルを宣言します.
(2)list_initとlist_add_tail
list_initとlist_add_tailの実装コードは/system/core/libcutils/listにあります.cでは、基本的な双方向チェーンテーブル操作が提供される.list_Initのソースコードは次のとおりです.
list_add_tailのソースコードは次のとおりです.
list_add_tailはitemを双方向チェーンテーブルの末尾に追加するだけです.
注意AndroidはLinuxカーネルでよく使われるチェーンテーブルの実現方法を参考にしています.チェーンテーブルのポインタ部分とデータ部分を分離します.
まずnode構造体を定義します.
3.4.4解析サービス(2)
このstruct listnodeの構造にチェーンテーブルの順方向ポインタと後項ポインタを入れます.異なるデータノードを処理する必要がある場合、このlistnodeを異なるデータノードに埋め込む.このようにチェーンテーブルを操作することは,このlistnodeを操作することであり,具体的なデータとは無関係である.parse_のようにサービス関数でlist_add_tail(&service_list, &svc->slist);サービスノードのlistnodeポインタ部分をservice_に入れるListチェーンテーブル.リストノードに対応するデータを操作する必要がある場合は、メンバーオフセット量によって対応するデータを見つけることができます.この部分は後で分析します.
(3)サービス構造体
parse_サービスで最も重要なデータ型はサービスであり、サービスというSectionの内容が格納されています.サービス構造体は/system/core/init/initで定義する.hでは、コードは以下の通りである.
サービスに必要な内容がたくさんあることがわかります.parse_サービス関数はサービスの基本情報を初期化しただけで、詳細はparse_line_サービス充填.
2.parse_line_service
parse_line_サービスのソースコードは次のとおりです.
ここまででServiceは解析が完了し,Actionの解析過程を解析する.
3.4.5解析Action
1.parse_action
解析アクションはまずparse_からAction関数が開始します.コードは次のとおりです.
parseからAction関数のコードから分かるように,Actionを解析する過程はサービスを解析する過程と非常に似ている.まず新しく作成したActionにストレージスペースを割り当て、Actionのポインタノードをaction_に挿入します.リストに表示されます.ここでは、action構造体とaction_の2つの重要なデータ型について説明します.Listチェーンテーブル.
action_リストとサービス_リストはすべてリスト_declareマクロ宣言、すなわちstatic list_declare(action_list).
Action構造体は/system/core/init/initで定義する.hでは、コードは以下の通りである.
2.parse_line_action
Actionの記憶形式を熟知し,次にActionの解析過程を解析する.parse_にナビゲートline_アクション関数init_にある関数parser.cでは、コードは以下の通りである.
parse_line_Action関数の実行プロセスはparse_よりも明確です.line_サービスは簡単です.
ここでは、重要なデータ型struct commandについて説明します.command構造体は/system/core/init/initで定義する.hでは、コードは以下の通りである.
これで、init.rcの解析過程は一段落した.次に、ActionとServiceの実行フェーズの分析を開始します.
3.5 ActionとServiceのトリガーと起動
解析するrc後,サービスとアクションを格納するチェーンテーブルが生成される.ではinitはどのようにしてこれらのActionとServiceを制御しているのでしょうか.このセクションでは、このセクションを詳細に分析します.
3.5.1アクションのトリガー
init解析完了init.rc後、action_を実行しましたfor_each_triggerとqueue_builtin_action.この2つの関数は何をしましたか.
まずaction_にナビゲートfor_each_trigger、実は現代コードはinit_にありますparser.cでは、コードは以下の通りである.
list_for_eachとnode_to_itemはいったい何をしたの?node_to_itemの2番目のパラメータstruct actionは何ですか?この2つの部分はlistに定義されています.hでは、そのコードは以下の通りである.
もとlist_for_eachは、forループを表すマクロです.node_to_itemのコードは次のとおりです.
node_to_itemはまたマクロであり、2番目のパラメータはcontainerによって識別されるパラメータを受け入れ、このパラメータはデータ型に置き換えられるので、コードにタイプstruct actionを直接入力することができます.
ここでは、C言語における非常に重要なマクロ定義:offsetofについて説明します.このマクロは、構造体のメンバーオフセット量が固定されているという特性を利用して、構造体のメンバーの構造体のオフセット量を求める.定義
このマクロ定義を詳細に分析します.
(TYPE*)0は、0を強制的にTYPE型に変換するポインタです.コンパイラにTYPEタイプを指すポインタがあることを伝えます.このポインタのアドレス値は0です.もちろんこれはすべてコンパイラをだましたので、この0アドレスを操作する必要はありませんので、間違いはありません.ptr=(TYPE*)0が定義されている場合、ptrはTYPEタイプを指すポインタであり、その基地局値は0である.では
ptr->MEMBERはMEMBERという要素であり、&(ptr->MEMBER)はMENBERのアドレスである.ベースアドレスが0である以上,MEMBERのアドレスはTYPEにおけるMEMBERのオフセット量である.最後に結果を強制的にsize_に変換t(size_tは実はunsigned int)ではMEMBERのオフセット量が得られる.offsetofの解析が完了し、actionに戻ります.for_each_trigger関数.node_to_item(node,struct action,alist)は、次のコードに置き換えられます.
(char*)(node)はchar*形式でnodeの値を読み出し、nodeにはalistのアドレスがあります.offsetof(struct action,alist)を次のコードに置き換えます.
ここでactionにおけるalistのオフセット量を得た.((char*)−offsetof(struct action,alist))は、このnodeに対応するActionのアドレスを取得し、最後にコンパイラにこのアドレスを(struct action*)形式で読み出すように伝え、nodeが存在するActionを取得し、nodeに対応するデータを見つけた.
次にaction_を分析しますadd_queue_tailで何をしましたか.コードは次のとおりです.
action_add_queue_tailではActionのqlistをaction_に入れただけですを選択します.アクションが見つかりましたqueueの宣言、サービスと発見listとaction_リストと同様にリスト_declareが宣言したマクロ.コードは次のとおりです.
queue_builtin_Actionの実行プロセスとaction_for_each_triggerは似ていて、最後にactionを呼び出しました.add_queue_tailとlist_add_tailメソッド、ここでは具体的に分析しません.
どうやらaction_for_each_triggerとqueue_builtin_Actionはサービスとアクションを実際に実行していません.
1.parse_service
解析サービス先parse_サービスが開始します.コードは次のとおりです.
- static void *parse_service(struct parse_state *state,int nargs, char **args)
- {
- struct service *svc;//service , Service
- ……//
- nargs -= 2;
- /* Service */
- svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
- /* service */
- svc->name = args[1];
- svc->classname = "default";
- memcpy(svc->args, args + 2, sizeof(char*) * nargs);
- svc->args[nargs] = 0;
- svc->nargsnargs = nargs;
- svc->onrestart.name = "onrestart";
- /* Service restart Option Commands ,
- * Servic slist service_list */
- list_init(&svc->onrestart.commands);
- /* Service service_list */
- list_add_tail(&service_list, &svc->slist);
- return svc;
- }
parse_サービス関数は主に3つの作業を行った:1)新しいサービスにストレージスペースを割り当て,2)サービスを初期化し,
3)サービスを1つのサービスに入れる_Listチェーンテーブル.重要なデータ型と関数はいくつかあります.service_list、list_initとlist_add_tail、およびservice構造体.
(1)service_list
service_リストはlist_からdeclare定義、list_declareは実際には/system/core/include/cutils/listにあるマクロです.h.ソースコードは次のとおりです.
- #define list_declare(name) \
- struct listnode name = { \
- .next = &name, \
- .prev = &name, \
- }
service_Listは、前方ポインタと後方ポインタを格納する双方向チェーンテーブルを宣言します.
(2)list_initとlist_add_tail
list_initとlist_add_tailの実装コードは/system/core/libcutils/listにあります.cでは、基本的な双方向チェーンテーブル操作が提供される.list_Initのソースコードは次のとおりです.
- void list_init(struct listnode *node)
- {
- node->next = node;
- node->prev = node;
- }
list_add_tailのソースコードは次のとおりです.
- void list_add_tail(struct listnode *head, struct listnode *item)
- {
- item->next = head;
- item->prev = head->prev;
- head->prev->next = item;
- head->prev = item;
- }
list_add_tailはitemを双方向チェーンテーブルの末尾に追加するだけです.
注意AndroidはLinuxカーネルでよく使われるチェーンテーブルの実現方法を参考にしています.チェーンテーブルのポインタ部分とデータ部分を分離します.
まずnode構造体を定義します.
- struct listnode
- {
- struct listnode *next;
- struct listnode *prev;
- };
-
3.4.4解析サービス(2)
このstruct listnodeの構造にチェーンテーブルの順方向ポインタと後項ポインタを入れます.異なるデータノードを処理する必要がある場合、このlistnodeを異なるデータノードに埋め込む.このようにチェーンテーブルを操作することは,このlistnodeを操作することであり,具体的なデータとは無関係である.parse_のようにサービス関数でlist_add_tail(&service_list, &svc->slist);サービスノードのlistnodeポインタ部分をservice_に入れるListチェーンテーブル.リストノードに対応するデータを操作する必要がある場合は、メンバーオフセット量によって対応するデータを見つけることができます.この部分は後で分析します.
(3)サービス構造体
parse_サービスで最も重要なデータ型はサービスであり、サービスというSectionの内容が格納されています.サービス構造体は/system/core/init/initで定義する.hでは、コードは以下の通りである.
- struct service {
- /* list of all services */
- struct listnode slist;
- const char *name; //Service
- const char *classname; //Service
- unsigned flags; //Service
- pid_t pid; //Service
- time_t time_started; //
- time_t time_crashed; //
- int nr_crashed; //
- uid_t uid; // ID
- gid_t gid; // ID
- gid_t supp_gids[NR_SVC_SUPP_GIDS];
- size_t nr_supp_gids;
- struct socketinfo *sockets; //Service Socket
- struct svcenvinfo *esnvvars; //Service
- /*Service Action。 onrestart Option。 onrestart
- * Option Command, Action Command , Action */
- struct action onrestart;
- /* service , /dev/keychord */
- int *keycodes;
- int nkeycodes;
- int keychord_id;
- /*IO , IO */
- int ioprio_class;
- int ioprio_pri;
- /* */
- int nargs;
- /* */
- char *args[1];
- }; /*args */
サービスに必要な内容がたくさんあることがわかります.parse_サービス関数はサービスの基本情報を初期化しただけで、詳細はparse_line_サービス充填.
2.parse_line_service
parse_line_サービスのソースコードは次のとおりです.
- static void parse_line_service(struct parse_state *state, int nargs, char **args)
- {
- /* state context Service */
- struct service *svc = state->context;
- struct command *cmd;
- int i, kw, kw_nargs;
- if (nargs == 0) {
- return;
- }
- svc->ioprio_class = IoSchedClass_NONE;
- /* lookup_keyword , Service Option */
- kw = lookup_keyword(args[0]);
- switch (kw) {
- case K_class:
- if (nargs != 2) {
- ……//
- }else {
- svc->classname = args[1];
- }
- break;
- ……// case
- case K_onrestart:
- nargs--;
- args++;
- kw = lookup_keyword(args[0]);
- ……//
- /* onrestart Option Command , list_add_tail */
- cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
- cmd->func = kw_func(kw);
- cmd->nargsnargs = nargs;
- memcpy(cmd->args, args, sizeof(char*) * nargs);
- list_add_tail(&svc->onrestart.commands, &cmd->clist);
- break;
- ……// case
- case K_socket: {/* name type perm [ uid gid ] */
- struct socketinfo *si;
- ……//
- /* Socket, Socket,socketinfo Socket */
- si = calloc(1, sizeof(*si));
- si->name = args[1];// Socket
- ……
- break;
- }
- ……// case
- default: // ,
- parse_error(state, "invalid option '%s'
", args[0]);
- }
- }
ここまででServiceは解析が完了し,Actionの解析過程を解析する.
3.4.5解析Action
1.parse_action
解析アクションはまずparse_からAction関数が開始します.コードは次のとおりです.
- static void *parse_action(struct parse_state *state, int nargs, char **args)
-
- {
- struct action *act;
- ……//
- act = calloc(1, sizeof(*act));
- act->name = args[1];
- list_init(&act->commands);
- /* Action action_list */
- list_add_tail(&action_list, &act->alist);
- return act;
- }
parseからAction関数のコードから分かるように,Actionを解析する過程はサービスを解析する過程と非常に似ている.まず新しく作成したActionにストレージスペースを割り当て、Actionのポインタノードをaction_に挿入します.リストに表示されます.ここでは、action構造体とaction_の2つの重要なデータ型について説明します.Listチェーンテーブル.
action_リストとサービス_リストはすべてリスト_declareマクロ宣言、すなわちstatic list_declare(action_list).
Action構造体は/system/core/init/initで定義する.hでは、コードは以下の通りである.
- struct action {
- /* Action */
- struct listnode alist;
- /* Action */
- struct listnode qlist;
- /* Action */
- struct listnode tlist;
- unsigned hash;
- const char *name;
- /*Action Command*/
- struct listnode commands;
- struct command *current;
- };
2.parse_line_action
Actionの記憶形式を熟知し,次にActionの解析過程を解析する.parse_にナビゲートline_アクション関数init_にある関数parser.cでは、コードは以下の通りである.
- static void parse_line_action(struct parse_state* state, int nargs, char **args)
- {
- struct command *cmd;
- /* state Action */
- struct action *act = state->context;
- int (*func)(int nargs, char **args);
- int kw, n;
- /* , Command */
- kw = lookup_keyword(args[0]);
- n = kw_nargs(kw);
- ……//
- cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
- cmd->func = kw_func(kw);// Command
- cmd->nargsnargs = nargs;
- memcpy(cmd->args, args, sizeof(char*) * nargs);
- /* Command Action Command */
- list_add_tail(&act->commands, &cmd->clist);
parse_line_Action関数の実行プロセスはparse_よりも明確です.line_サービスは簡単です.
ここでは、重要なデータ型struct commandについて説明します.command構造体は/system/core/init/initで定義する.hでは、コードは以下の通りである.
- struct command
- {
- /* list of commands in an action */
- struct listnode clist;
- /* command */
- int (*func)(int nargs, char **args);
- int nargs;
- char *args[1];
- };
これで、init.rcの解析過程は一段落した.次に、ActionとServiceの実行フェーズの分析を開始します.
3.5 ActionとServiceのトリガーと起動
解析するrc後,サービスとアクションを格納するチェーンテーブルが生成される.ではinitはどのようにしてこれらのActionとServiceを制御しているのでしょうか.このセクションでは、このセクションを詳細に分析します.
3.5.1アクションのトリガー
init解析完了init.rc後、action_を実行しましたfor_each_triggerとqueue_builtin_action.この2つの関数は何をしましたか.
まずaction_にナビゲートfor_each_trigger、実は現代コードはinit_にありますparser.cでは、コードは以下の通りである.
- void action_for_each_trigger(const char *trigger, void (*func)(struct action *act))
- {
- struct listnode *node;
- struct action *act;
- /* , node_to_item */
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- if (!strcmp(act->name, trigger)) {
- func(act);// func
- }
- }
- }
list_for_eachとnode_to_itemはいったい何をしたの?node_to_itemの2番目のパラメータstruct actionは何ですか?この2つの部分はlistに定義されています.hでは、そのコードは以下の通りである.
- #define list_for_each(node, list) \
- for (node = (list)->next; node != (list); nodenode = node->next)
もとlist_for_eachは、forループを表すマクロです.node_to_itemのコードは次のとおりです.
- #define node_to_item(node, container, member) \
- (container *) (((char*) (node)) - offsetof(container, member))
node_to_itemはまたマクロであり、2番目のパラメータはcontainerによって識別されるパラメータを受け入れ、このパラメータはデータ型に置き換えられるので、コードにタイプstruct actionを直接入力することができます.
ここでは、C言語における非常に重要なマクロ定義:offsetofについて説明します.このマクロは、構造体のメンバーオフセット量が固定されているという特性を利用して、構造体のメンバーの構造体のオフセット量を求める.定義
- /bionic/libc/kernel/common/linux/stddef.h , :
- #ifdef __compiler_offsetof
- #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
- #else
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
- #endif
このマクロ定義を詳細に分析します.
(TYPE*)0は、0を強制的にTYPE型に変換するポインタです.コンパイラにTYPEタイプを指すポインタがあることを伝えます.このポインタのアドレス値は0です.もちろんこれはすべてコンパイラをだましたので、この0アドレスを操作する必要はありませんので、間違いはありません.ptr=(TYPE*)0が定義されている場合、ptrはTYPEタイプを指すポインタであり、その基地局値は0である.では
ptr->MEMBERはMEMBERという要素であり、&(ptr->MEMBER)はMENBERのアドレスである.ベースアドレスが0である以上,MEMBERのアドレスはTYPEにおけるMEMBERのオフセット量である.最後に結果を強制的にsize_に変換t(size_tは実はunsigned int)ではMEMBERのオフセット量が得られる.offsetofの解析が完了し、actionに戻ります.for_each_trigger関数.node_to_item(node,struct action,alist)は、次のコードに置き換えられます.
- (struct action *) (((char*) (node)) - offsetof(struct action, alist))
(char*)(node)はchar*形式でnodeの値を読み出し、nodeにはalistのアドレスがあります.offsetof(struct action,alist)を次のコードに置き換えます.
- ((size_t) &(( struct action *)0)-> alist)
ここでactionにおけるalistのオフセット量を得た.((char*)−offsetof(struct action,alist))は、このnodeに対応するActionのアドレスを取得し、最後にコンパイラにこのアドレスを(struct action*)形式で読み出すように伝え、nodeが存在するActionを取得し、nodeに対応するデータを見つけた.
次にaction_を分析しますadd_queue_tailで何をしましたか.コードは次のとおりです.
- void action_add_queue_tail(struct action *act)
- {
-
- list_add_tail(&action_queue, &act->qlist);
- }
action_add_queue_tailではActionのqlistをaction_に入れただけですを選択します.アクションが見つかりましたqueueの宣言、サービスと発見listとaction_リストと同様にリスト_declareが宣言したマクロ.コードは次のとおりです.
- static list_declare(action_queue);
queue_builtin_Actionの実行プロセスとaction_for_each_triggerは似ていて、最後にactionを呼び出しました.add_queue_tailとlist_add_tailメソッド、ここでは具体的に分析しません.
どうやらaction_for_each_triggerとqueue_builtin_Actionはサービスとアクションを実際に実行していません.