Android啟動流程分析(九) 解析init.rc的service
#############################################
本文為極度寒冰原創,轉載請註明出處
#############################################
在分析完解析init.rc的action之後,剩下的一部分就是解析service了。
而解析service還是需要回到parse_config裡面來。根據前面的知識,我們也可以很容易的知道在關鍵字為section的時候,會進入到parse_new_section。
這裡會先執行parse_service,然後將service以及後面跟的option設定為執行parse_line:parse_line_service。
要理解service的解析流程的話,首先要關注的就是service的結構體。
struct service { /* list of all services */ struct listnode slist; // listnode slist const char *name; // name const char *classname; // 預設值為defult unsigned flags; // 選項 pid_t pid; // Service所在程序的PID time_t time_started; /* time of last start */ time_t time_crashed; /* first crash within inspection window */ int nr_crashed; /* number of times crashed within window */ uid_t uid; // effective user ID gid_t gid; // effective group ID gid_t supp_gids[NR_SVC_SUPP_GIDS]; // supplementary ids size_t nr_supp_gids; // supp_gids的大小 char *seclabel; struct socketinfo *sockets; // 為service建立的Sockets struct svcenvinfo *envvars; // 為service設定的環境變數 struct action onrestart; /* Actions to execute on restart. */ /* keycodes for triggering this service via /dev/keychord */ int *keycodes; int nkeycodes; int keychord_id; int ioprio_class; int ioprio_pri; int nargs; /* "MUST BE AT THE END OF THE STRUCT" */ char *args[1]; }; /* ^-------'args' MUST be at the end of this struct! */
這個結構體相比較而言就比較簡單了,除了service的本身屬性以外,對於資料結構方面就只有一個listnode。
struct listnode slist;
這也就是說,在service的結構體中,這個結構體只會被加入一條連結串列而不是像action的兩條連結串列。
另外需要注意的是,在service的結構體中,也維護了一個action的結構體,這就是說,在service中,也存在著一個action的commands的連結串列?
然後我們就來看看parse_service的函式
從上面我們知道,在執行完parse_service之後,會初始化service的一些屬性,將service servicename之後的做為args進行儲存。static void *parse_service(struct parse_state *state, int nargs, char **args) { struct service *svc; // 宣告結構體 if (nargs < 3) { // 如果service的引數小於三個的話,我們會認為service是個不正常的service。service最少的nargs也是3,分別為service關鍵字,service的名字,service啟動的時候要執行的命令 parse_error(state, "services must have a name and a program\n"); return 0; } if (!valid_name(args[1])) { //如果service的name為不標準的名字的話,含有其它的符號的話,我們會認為這個service是不規範的service。 parse_error(state, "invalid service name '%s'\n", args[1]); return 0; } svc = service_find_by_name(args[1]); 。。// 會從已經存在的service_list裡面去查詢,是否已經有同名的service存在 if (svc) { // 如果發現有同名的service存在的話,則會返回error parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); return 0; } nargs -= 2; // 去除service關鍵字與service的name svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); // malloc這個結構體 if (!svc) { // 如果malloc失敗的話,提示out of memory parse_error(state, "out of memory\n"); return 0; } svc->name = args[1]; // 設定service的name為service關鍵字後的第一個引數 svc->classname = "default"; // 預設的classname為default memcpy(svc->args, args + 2, sizeof(char*) * nargs); // 將args剩餘的引數複製到svc的args裡面 svc->args[nargs] = 0; // 給args的最後一項設定為0 svc->nargs = nargs; // 引數的數目等於傳進來的引數的數目 svc->onrestart.name = "onrestart"; // 設定onrestart.name為onrestart list_init(&svc->onrestart.commands); // 初始化onrestart的連結串列 list_add_tail(&service_list, &svc->slist); // 將當前的service結構體加入到了service_list的連結串列裡面 return svc; }
另外,將這個解析出來的service加入到service_list的連結串列裡面。
然後接下來,去執行的就是
state->parse_line = parse_line_service;
那我們像action,再來看看parse_line_service的操作
static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
struct service *svc = state->context;
struct command *cmd;
int i, kw, kw_nargs;
if (nargs == 0) {
return;
}
svc->ioprio_class = IoSchedClass_NONE;
kw = lookup_keyword(args[0]);
switch (kw) {
case K_capability:
break;
case K_class:
if (nargs != 2) {
parse_error(state, "class option requires a classname\n");
} else {
svc->classname = args[1];
}
break;
case K_console:
svc->flags |= SVC_CONSOLE;
break;
case K_disabled:
svc->flags |= SVC_DISABLED;
svc->flags |= SVC_RC_DISABLED;
break;
case K_ioprio:
if (nargs != 3) {
parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
} else { 。。。。 ........ case K_onrestart:
nargs--;
args++;
kw = lookup_keyword(args[0]);
if (!kw_is(kw, COMMAND)) {
parse_error(state, "invalid command '%s'\n", args[0]);
break;
}
kw_nargs = kw_nargs(kw);
if (nargs < kw_nargs) {
parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
kw_nargs > 2 ? "arguments" : "argument");
break;
}
cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
cmd->func = kw_func(kw);
cmd->nargs = nargs;
memcpy(cmd->args, args, sizeof(char*) * nargs);
list_add_tail(&svc->onrestart.commands, &cmd->clist);
break; ............... }
可以看到,parse_line_service的這個函式,主要就是將解析service的每一行,將其對應進不同的case裡面,進行service結構體的填充。
可能這樣講,理解起來會有困難。
但是為了方便理解,我們從init.rc裡面找個例子出來分析:
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
在parse_line_service的時候,會在
class的關鍵字的時候,執行到
case K_class:
if (nargs != 2) { // 判斷是否是兩個token,如果不是的話,格式錯誤
parse_error(state, "class option requires a classname\n");
} else {
svc->classname = args[1];
}
break;
也就是將service的classname從"default"修改為“args[1]”在user system的的option的時候,
會去執行
case K_user:
if (nargs != 2) {
parse_error(state, "user option requires a user id\n");
} else {
svc->uid = decode_uid(args[1]);
}
break;
會將uid設定為system對應的uid
另外需要注意的是,在解析的過程中,會有一個比較重要的option是restart,來看一下當執行到這個關鍵字的時候的時候,會執行什麼。
case K_onrestart:
nargs--;
args++;
kw = lookup_keyword(args[0]);
if (!kw_is(kw, COMMAND)) { // 判斷是否是command,如果restart之後跟的不是command的話,就會返回error
parse_error(state, "invalid command '%s'\n", args[0]);
break;
}
kw_nargs = kw_nargs(kw); // 獲得當前command所需的引數
if (nargs < kw_nargs) { // 如果傳遞的引數小於我們需要的引數的話,會返回error
parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
kw_nargs > 2 ? "arguments" : "argument");
break;
}
cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); // 初始化command
cmd->func = kw_func(kw); // 將kw所包含的func賦值給cmd->func
cmd->nargs = nargs; // 將引數的個數儲存為nargs
memcpy(cmd->args, args, sizeof(char*) * nargs); // 將這些引數複製到cmd的args中
list_add_tail(&svc->onrestart.commands, &cmd->clist); // 將這些command加入到service結構體的內部連結串列中
break;
至此,我們分析了所有關於init.rc的解析問題。
在service的解析後,會生成一條連結串列儲存service的結構體,然後service的結構體裡面自己執行維護一個action。
這個action會包含所有的restatt包含的內容,也就是restart的option關鍵字後會包含要執行的command
這個的結構應該比action的要簡單和明瞭的多。
分析完了解析,我們應該去看一下android是如何在啟動的過程中去執行這些action和service的。