Androidのinitプロセスの詳細(三)

107705 ワード

解析サービス(1)
1.parse_service
解析サービス先parse_サービスが開始します.コードは次のとおりです.

  
  
  
  
  1. static void *parse_service(struct parse_state *state,int nargs, char **args)  
  2. {  
  3.   struct service *svc;//service , Service  
  4.   ……//  
  5.   nargs -2;  
  6.   /* Service */  
  7.   svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);  
  8.   /* service */  
  9.   svc->name = args[1];  
  10.   svc->classname = "default";  
  11.   memcpy(svc->args, args + 2, sizeof(char*) * nargs);  
  12.   svc->args[nargs] = 0;  
  13.   svc->nargsnargs = nargs;  
  14.   svc->onrestart.name = "onrestart";  
  15.   /* Service restart Option Commands ,  
  16.    * Servic slist service_list */  
  17.   list_init(&svc->onrestart.commands);  
  18.   /* Service service_list */  
  19.   list_add_tail(&service_list, &svc->slist);  
  20.   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.ソースコードは次のとおりです.

  
  
  
  
  1. #define list_declare(name) \  
  2. struct listnode name = { \  
  3.    .next = &name, \  
  4.    .prev = &name, \  

service_Listは、前方ポインタと後方ポインタを格納する双方向チェーンテーブルを宣言します.
(2)list_initとlist_add_tail
list_initとlist_add_tailの実装コードは/system/core/libcutils/listにあります.cでは、基本的な双方向チェーンテーブル操作が提供される.list_Initのソースコードは次のとおりです.

  
  
  
  
  1. void list_init(struct listnode *node)  
  2. {  
  3.    node->next = node;  
  4.    node->prev = node;  

list_add_tailのソースコードは次のとおりです.

  
  
  
  
  1. void list_add_tail(struct listnode *head, struct listnode *item)  
  2. {  
  3.    item->next = head;  
  4.    item->prev = head->prev;  
  5.    head->prev->next = item;  
  6.    head->prev = item;  

list_add_tailはitemを双方向チェーンテーブルの末尾に追加するだけです.
注意AndroidはLinuxカーネルでよく使われるチェーンテーブルの実現方法を参考にしています.チェーンテーブルのポインタ部分とデータ部分を分離します.
まずnode構造体を定義します.

  
  
  
  
  1. struct listnode  
  2. {  
  3. struct listnode *next;  
  4. struct listnode *prev;  
  5. };  

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では、コードは以下の通りである.

  
  
  
  
  1. struct service {  
  2.    /* list of all services */  
  3.    struct listnode slist;  
  4.    const char *name;  //Service  
  5.    const char *classname;  //Service  
  6.    unsigned flags;   //Service  
  7.    pid_t pid;   //Service  
  8.    time_t time_started;  //  
  9.    time_t time_crashed;  //  
  10.    int nr_crashed;   //  
  11.    uid_t uid;   // ID  
  12.    gid_t gid;   // ID  
  13.    gid_t supp_gids[NR_SVC_SUPP_GIDS];  
  14.    size_t nr_supp_gids;  
  15.    struct socketinfo *sockets;  //Service Socket  
  16.    struct svcenvinfo *esnvvars;  //Service  
  17.    /*Service Action。 onrestart Option。 onrestart  
  18. * Option Command, Action Command , Action */  
  19. struct action onrestart;  
  20. /*  service  , /dev/keychord  */  
  21. int *keycodes;  
  22. int nkeycodes;  
  23. int keychord_id;  
  24. /*IO , IO */  
  25. int ioprio_class;  
  26. int ioprio_pri;  
  27. /* */  
  28. int nargs;  
  29. /* */  
  30. char *args[1];  
  31. }; /*args   */ 

サービスに必要な内容がたくさんあることがわかります.parse_サービス関数はサービスの基本情報を初期化しただけで、詳細はparse_line_サービス充填.
2.parse_line_service
parse_line_サービスのソースコードは次のとおりです.

  
  
  
  
  1. static void parse_line_service(struct parse_state *state, int nargs, char **args)  
  2. {  
  3. /*  state context Service */  
  4. struct service *svc = state->context;  
  5. struct command *cmd;  
  6. int i, kw, kw_nargs;  
  7. if (nargs == 0) {  
  8.    return;  
  9. }  
  10. svc->ioprio_class = IoSchedClass_NONE;  
  11. /*  lookup_keyword , Service Option */  
  12. kw = lookup_keyword(args[0]);  
  13. switch (kw) {  
  14. case K_class:  
  15.    if (nargs != 2) {  
  16.       ……//  
  17.      }else {  
  18.       svc->classname = args[1];  
  19.     }  
  20.     break;  
  21. ……// case  
  22. case K_onrestart:  
  23.    nargs--;  
  24.    args++;  
  25.    kw = lookup_keyword(args[0]);  
  26.    ……//  
  27.   /* onrestart Option Command , list_add_tail */  
  28.   cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);  
  29.   cmd->func = kw_func(kw);  
  30.   cmd->nargsnargs = nargs;  
  31.   memcpy(cmd->args, args, sizeof(char*) * nargs);  
  32.   list_add_tail(&svc->onrestart.commands, &cmd->clist);  
  33.   break;  
  34.  ……// case  
  35.  case K_socket: {/* name type perm [ uid gid ] */  
  36.     struct socketinfo *si;  
  37.     ……//  
  38.  /* Socket, Socket,socketinfo Socket */  
  39.  si = calloc(1, sizeof(*si));  
  40.  si->name = args[1];// Socket  
  41.  ……  
  42.  break;  
  43.   }  
  44.   ……// case  
  45.   default: // ,  
  46. parse_error(state, "invalid option '%s'
    ", args[0]);  
  47.   }  

ここまででServiceは解析が完了し,Actionの解析過程を解析する. 
3.4.5解析Action
1.parse_action
解析アクションはまずparse_からAction関数が開始します.コードは次のとおりです.

  
  
  
  
  1. static void *parse_action(struct parse_state *state, int nargs, char **args)  
  2.  
  3. {  
  4.    struct action *act;  
  5.    ……//  
  6.    act = calloc(1, sizeof(*act));  
  7.    act->name = args[1];  
  8.    list_init(&act->commands);  
  9.    /* Action action_list */  
  10.    list_add_tail(&action_list, &act->alist);  
  11.    return act;  
  12. }  

parseからAction関数のコードから分かるように,Actionを解析する過程はサービスを解析する過程と非常に似ている.まず新しく作成したActionにストレージスペースを割り当て、Actionのポインタノードをaction_に挿入します.リストに表示されます.ここでは、action構造体とaction_の2つの重要なデータ型について説明します.Listチェーンテーブル.
action_リストとサービス_リストはすべてリスト_declareマクロ宣言、すなわちstatic list_declare(action_list).
Action構造体は/system/core/init/initで定義する.hでは、コードは以下の通りである.

  
  
  
  
  1. struct action {  
  2.    /* Action  */  
  3.    struct listnode alist;  
  4.    /* Action */  
  5.    struct listnode qlist;  
  6.    /* Action */  
  7.    struct listnode tlist;  
  8.    unsigned hash;  
  9.    const char *name;  
  10.    /*Action Command*/  
  11.    struct listnode commands;  
  12.    struct command *current;  
  13. }; 

2.parse_line_action
Actionの記憶形式を熟知し,次にActionの解析過程を解析する.parse_にナビゲートline_アクション関数init_にある関数parser.cでは、コードは以下の通りである.

  
  
  
  
  1. static void parse_line_action(struct parse_state* state, int nargs, char **args)  
  2. {  
  3. struct command *cmd;  
  4. /* state Action */  
  5. struct action *act = state->context;  
  6. int (*func)(int nargs, char **args);  
  7. int kw, n;  
  8. /* , Command */  
  9. kw = lookup_keyword(args[0]);  
  10. n = kw_nargs(kw);  
  11. ……//  
  12. cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);  
  13. cmd->func = kw_func(kw);// Command  
  14. cmd->nargsnargs = nargs;  
  15. memcpy(cmd->args, args, sizeof(char*) * nargs);  
  16. /* Command Action Command */  
  17. list_add_tail(&act->commands, &cmd->clist); 

parse_line_Action関数の実行プロセスはparse_よりも明確です.line_サービスは簡単です.
ここでは、重要なデータ型struct commandについて説明します.command構造体は/system/core/init/initで定義する.hでは、コードは以下の通りである.

  
  
  
  
  1. struct command  
  2. {  
  3.    /* list of commands in an action */  
  4.    struct listnode clist;  
  5.    /* command */  
  6.    int (*func)(int nargs, char **args);  
  7.    int nargs;  
  8.    char *args[1];  
  9. }; 

これで、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では、コードは以下の通りである.

  
  
  
  
  1. void action_for_each_trigger(const char *trigger, void (*func)(struct action *act))  
  2. {  
  3. struct listnode *node;  
  4. struct action *act;  
  5. /* , node_to_item */  
  6. list_for_each(node, &action_list) {  
  7.     act = node_to_item(node, struct action, alist);  
  8.     if (!strcmp(act->name, trigger)) {  
  9.        func(act);// func  
  10.     }  
  11. }  

list_for_eachとnode_to_itemはいったい何をしたの?node_to_itemの2番目のパラメータstruct actionは何ですか?この2つの部分はlistに定義されています.hでは、そのコードは以下の通りである.

  
  
  
  
  1. #define list_for_each(node, list) \  
  2. for (node = (list)->next; node != (list); nodenode = node->next) 

もとlist_for_eachは、forループを表すマクロです.node_to_itemのコードは次のとおりです.

  
  
  
  
  1. #define node_to_item(node, container, member) \  
  2.    (container *) (((char*) (node)) - offsetof(container, member)) 

node_to_itemはまたマクロであり、2番目のパラメータはcontainerによって識別されるパラメータを受け入れ、このパラメータはデータ型に置き換えられるので、コードにタイプstruct actionを直接入力することができます.
ここでは、C言語における非常に重要なマクロ定義:offsetofについて説明します.このマクロは、構造体のメンバーオフセット量が固定されているという特性を利用して、構造体のメンバーの構造体のオフセット量を求める.定義

  
  
  
  
  1. /bionic/libc/kernel/common/linux/stddef.h , :  
  2. #ifdef __compiler_offsetof  
  3. #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)  
  4. #else  
  5. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)  
  6. #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)は、次のコードに置き換えられます.

  
  
  
  
  1. (struct action *) (((char*) (node)) - offsetof(struct action, alist)) 

(char*)(node)はchar*形式でnodeの値を読み出し、nodeにはalistのアドレスがあります.offsetof(struct action,alist)を次のコードに置き換えます.

  
  
  
  
  1. ((size_t) &(( struct action *)0)-> alist) 

ここでactionにおけるalistのオフセット量を得た.((char*)−offsetof(struct action,alist))は、このnodeに対応するActionのアドレスを取得し、最後にコンパイラにこのアドレスを(struct action*)形式で読み出すように伝え、nodeが存在するActionを取得し、nodeに対応するデータを見つけた.
次にaction_を分析しますadd_queue_tailで何をしましたか.コードは次のとおりです.

  
  
  
  
  1. void action_add_queue_tail(struct action *act)  
  2. {  
  3.  
  4. list_add_tail(&action_queue, &act->qlist);  
  5. }  

action_add_queue_tailではActionのqlistをaction_に入れただけですを選択します.アクションが見つかりましたqueueの宣言、サービスと発見listとaction_リストと同様にリスト_declareが宣言したマクロ.コードは次のとおりです.

  
  
  
  
  1. 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はサービスとアクションを実際に実行していません.