Android init.rc分析
1 前言
什么是init.rc文件?
import /init.usb.rc import /init.${ro.hardware}.rc import /init.trace.rcon early-init# Set init and its forked children's oom_adj.write /proc/1/oom_adj -16# Set the security context for the init process.# This should occur before anything else (e.g. ueventd) is started.setcon u:r:init:s0start ueventd# create mountpointsmkdir /mnt 0775 root systemon initsysclktz 0loglevel 3......on fs # mount mtd partitions# Mount /system rw first to give the filesystem a chance to save a checkpointmount yaffs2 mtd@system /systemmount yaffs2 mtd@system /system ro remountmount yaffs2 mtd@userdata /data nosuid nodevmount yaffs2 mtd@cache /cache nosuid nodevon post-fs......on post-fs-data......on boot....... on nonencryptedclass_start late_starton chargerclass_start chargeron property:vold.decrypt=trigger_reset_mainclass_reset main...... service servicemanager /system/bin/servicemanagerclass coreuser systemgroup systemcriticalonrestart restart zygoteonrestart restart mediaonrestart restart surfaceflingeronrestart restart drmservice vold /system/bin/voldclass coresocket vold stream 0660 root mountioprio be 2service netd /system/bin/netdclass mainsocket netd stream 0660 root systemsocket dnsproxyd stream 0660 root inetsocket mdns stream 0660 root systemservice debuggerd /system/bin/debuggerd......上面的就是一個(gè)init.rc的片段。可以在Android源代碼中找到,位置在/system/core/rootdir/
init.rc是Android系統(tǒng)/init程序讀取的初始化配置文件,用于啟動(dòng)Android中的各種服務(wù),以及配置系統(tǒng)。
2?init.rc分析
文件使用的腳本格式被稱作“Android Init Language”(AIL)。
AIL的解析以由空格(Whitespace)分隔的token組成的行為基本單位,這些行由四種類型組成,——Action、Command、Service、Option。
如果一行最開始是"#",那么這一行是注釋。
在/init的main函數(shù)中如下調(diào)用函數(shù)解析該文件:
INFO("reading config file\n"); init_parse_config_file("/init.rc");函數(shù)init_parse_config_file(/system/core/init/init_parser.c)
int init_parse_config_file(const char *fn) {char *data;data = read_file(fn, 0);if (!data) return -1;parse_config(fn, data);DUMP();return 0; }該函數(shù)過程如下:
- 首先調(diào)用函數(shù)read_file(在/system/core/init/util.c中),把/init.rc配置文件讀到data^data變量中,同時(shí)確保文件以“\n \0”結(jié)尾
- 最后,也是最關(guān)鍵的部分,調(diào)用parse_config函數(shù)進(jìn)行配置文件分析。
為了更好的了解/init.rc的配置究竟是什么,我們必須仔細(xì)分析parse_config函數(shù)。
函數(shù)parse_config(/system/core/init/init_parser.c)
static void parse_config(const char *fn, char *s) {struct parse_state state;struct listnode import_list;struct listnode *node;char *args[INIT_PARSER_MAXARGS];//用于存儲(chǔ)每一行的token字符int nargs;nargs = 0;state.filename = fn;state.line = 0;state.ptr = s;state.nexttoken = 0;state.parse_line = parse_line_no_op;list_init(&import_list);state.priv = &import_list;for (;;) {switch (next_token(&state)) {case T_EOF:state.parse_line(&state, 0, 0);goto parser_done;case T_NEWLINE:state.line++;if (nargs) {int kw = lookup_keyword(args[0]);if (kw_is(kw, SECTION)) {state.parse_line(&state, 0, 0);parse_new_section(&state, kw, nargs, args);} else {state.parse_line(&state, nargs, args);}nargs = 0;}break;case T_TEXT:if (nargs < INIT_PARSER_MAXARGS) {args[nargs++] = state.text;}break;}}parser_done:list_for_each(node, &import_list) {struct import *import = node_to_item(node, struct import, list);int ret;INFO("importing '%s'", import->filename);ret = init_parse_config_file(import->filename);......} }該函數(shù)有兩個(gè)重要的數(shù)據(jù)結(jié)構(gòu)
struct parse_state定義(/system/core/init/parser.h)
有點(diǎn)編譯原理基礎(chǔ)的就會(huì)明白,這就是parser的狀態(tài)。
還有
在以行單位的解析中,把每一行解析的token字符串指針存放在args字符指針數(shù)組中,把當(dāng)前行中的token個(gè)數(shù)存放在nargs中。
在函數(shù)parse_config中通過switch結(jié)構(gòu)進(jìn)行狀態(tài)的轉(zhuǎn)換。各種狀態(tài)如下。
| T_EOF | 代表文件的結(jié)束 |
| T_NEWLINE | 代表新的一行,所以需要解析當(dāng)前行 |
| T_TEXT | 代表一個(gè)token,把指針放到對(duì)應(yīng)的args,并更新nargs |
重點(diǎn)分析狀態(tài)T_NEWLINE.
case T_NEWLINE:state.line++;if (nargs) {int kw = lookup_keyword(args[0]);if (kw_is(kw, SECTION)) {state.parse_line(&state, 0, 0);parse_new_section(&state, kw, nargs, args);} else {state.parse_line(&state, nargs, args);}nargs = 0;}break;首先通過lookup_keyworkd和kw_is函數(shù),查找本行的第一個(gè)token是什么類型的,如果是Section,就調(diào)用parse_new_section,否則調(diào)用行處理函數(shù)。
疑問來了,state.parse_line不是在開始賦值為parse_line_no_op的空函數(shù)了嗎?調(diào)用空函數(shù)有什么意義啊?
其實(shí),秘密就在parse_new_section函數(shù)中。我們一步一步來解釋。 首先解決什么是Section,這就需要分析lookup_keyword和kw_is函數(shù)了。
該函數(shù)根據(jù)字符串返回一一對(duì)應(yīng)的枚舉類型。kw_is是宏:
#define kw_is(kw, type) (keyword_info[kw].flags & (type))keyword_info是全局變量定義如下:(/system/core/init/keywords.h)
#define KEYWORD(symbol, flags, nargs, func) \[ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, struct {const char *name;int (*func)(int nargs, char **args);unsigned char nargs;unsigned char flags; } keyword_info[KEYWORD_COUNT] = {[ K_UNKNOWN ] = { "unknown", 0, 0, 0 },KEYWORD(capability, OPTION, 0, 0)KEYWORD(chdir, COMMAND, 1, do_chdir)KEYWORD(chroot, COMMAND, 1, do_chroot)KEYWORD(class, OPTION, 0, 0)KEYWORD(class_start, COMMAND, 1, do_class_start)KEYWORD(class_stop, COMMAND, 1, do_class_stop)KEYWORD(class_reset, COMMAND, 1, do_class_reset)KEYWORD(console, OPTION, 0, 0)KEYWORD(critical, OPTION, 0, 0)KEYWORD(disabled, OPTION, 0, 0)KEYWORD(domainname, COMMAND, 1, do_domainname)KEYWORD(exec, COMMAND, 1, do_exec)KEYWORD(export, COMMAND, 2, do_export)KEYWORD(group, OPTION, 0, 0)KEYWORD(hostname, COMMAND, 1, do_hostname)KEYWORD(ifup, COMMAND, 1, do_ifup)KEYWORD(insmod, COMMAND, 1, do_insmod)KEYWORD(import, SECTION, 1, 0)KEYWORD(keycodes, OPTION, 0, 0)KEYWORD(mkdir, COMMAND, 1, do_mkdir)KEYWORD(mount_all, COMMAND, 1, do_mount_all)KEYWORD(mount, COMMAND, 3, do_mount)KEYWORD(on, SECTION, 0, 0)KEYWORD(oneshot, OPTION, 0, 0)KEYWORD(onrestart, OPTION, 0, 0)KEYWORD(restart, COMMAND, 1, do_restart)KEYWORD(restorecon, COMMAND, 1, do_restorecon)KEYWORD(rm, COMMAND, 1, do_rm)KEYWORD(rmdir, COMMAND, 1, do_rmdir)KEYWORD(seclabel, OPTION, 0, 0)KEYWORD(service, SECTION, 0, 0)KEYWORD(setcon, COMMAND, 1, do_setcon)KEYWORD(setenforce, COMMAND, 1, do_setenforce)KEYWORD(setenv, OPTION, 2, 0)KEYWORD(setkey, COMMAND, 0, do_setkey)KEYWORD(setprop, COMMAND, 2, do_setprop)KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)KEYWORD(setsebool, COMMAND, 1, do_setsebool)KEYWORD(socket, OPTION, 0, 0)KEYWORD(start, COMMAND, 1, do_start)KEYWORD(stop, COMMAND, 1, do_stop)KEYWORD(trigger, COMMAND, 1, do_trigger)KEYWORD(symlink, COMMAND, 1, do_symlink)KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)KEYWORD(user, OPTION, 0, 0)KEYWORD(wait, COMMAND, 1, do_wait)KEYWORD(write, COMMAND, 2, do_write)KEYWORD(copy, COMMAND, 2, do_copy)KEYWORD(chown, COMMAND, 2, do_chown)KEYWORD(chmod, COMMAND, 2, do_chmod)KEYWORD(loglevel, COMMAND, 1, do_loglevel)KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)KEYWORD(ioprio, OPTION, 0, 0) };關(guān)鍵字import、on、service代表新的Section的開始。 所以init.rc文件的結(jié)構(gòu)如下:
- 最高層由Section組成,分為trigger、import、service,分別以on,?import,service關(guān)鍵字開頭。
- importSection只有一行,至于載入其他rc文件
- triggerSection由?Command組成。
- serviceSection有?Option組成。
接下來我們直擊核心函數(shù)parse_new_section
void parse_new_section(struct parse_state *state, int kw,int nargs, char **args) {printf("[ %s %s ]\n", args[0],nargs > 1 ? args[1] : "");switch(kw) {case K_service:state->context = parse_service(state, nargs, args);if (state->context) {state->parse_line = parse_line_service;return;}break;case K_on:state->context = parse_action(state, nargs, args);if (state->context) {state->parse_line = parse_line_action;return;}break;case K_import:parse_import(state, nargs, args);break;}state->parse_line = parse_line_no_op; }在該函數(shù)中可以明顯看到Section的種類,以及state.parse_line被更改。接下來我們按Section種類,分三部分分析。
但是在這之前先介紹三個(gè)數(shù)據(jù)結(jié)構(gòu):
這三個(gè)全局變量都是鏈表的表頭,是/init對(duì)/init.rc解析所要操作的關(guān)鍵函數(shù),也可以說是解析的目的所在。service_list代表解析得到的Service,action_list代表解析得到的Action,action_queue代表將要執(zhí)行的Action隊(duì)列。
/init可以認(rèn)為主要是做了如下工作:
Section Serivice
每個(gè)Service,由一個(gè)struct service數(shù)據(jù)結(jié)構(gòu)代表,定義如下:
struct service {/* list of all services */struct listnode slist;//連接到service_listconst char *name;const char *classname;unsigned flags;pid_t 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;gid_t gid;gid_t supp_gids[NR_SVC_SUPP_GIDS];size_t nr_supp_gids;#ifdef HAVE_SELINUXchar *seclabel; #endifstruct socketinfo *sockets;struct svcenvinfo *envvars;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 thi函數(shù)parse_service(/system/core/init/init_parser.c)
static void *parse_service(struct parse_state *state, int nargs, char **args) {struct service *svc;if (nargs < 3) {parse_error(state, "services must have a name and a program\n");return 0;}if (!valid_name(args[1])) {parse_error(state, "invalid service name '%s'\n", args[1]);return 0;}svc = service_find_by_name(args[1]);if (svc) {parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);return 0;}nargs -= 2;svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);if (!svc) {parse_error(state, "out of memory\n");return 0;}svc->name = args[1];svc->classname = "default";memcpy(svc->args, args + 2, sizeof(char*) * nargs);svc->args[nargs] = 0;svc->nargs = nargs;svc->onrestart.name = "onrestart";list_init(&svc->onrestart.commands);list_add_tail(&service_list, &svc->slist);return svc; }首先本Section的第一行必須是如下格式
service <service name> <program name>而且在valid_name函數(shù)中規(guī)定,service name必須不超過16個(gè)字符,而且只能由字母、數(shù)字、“-”、“_”組成。
當(dāng)出現(xiàn)重名的service時(shí),會(huì)被忽略。
最后把Service掛到service_list尾部
下面分析parse_line_service函數(shù)
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 {svc->ioprio_pri = strtoul(args[2], 0, 8);if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {parse_error(state, "priority value must be range 0 - 7\n");break;}if (!strcmp(args[1], "rt")) {svc->ioprio_class = IoSchedClass_RT;} else if (!strcmp(args[1], "be")) {svc->ioprio_class = IoSchedClass_BE;} else if (!strcmp(args[1], "idle")) {svc->ioprio_class = IoSchedClass_IDLE;} else {parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");}}break;case K_group:if (nargs < 2) {parse_error(state, "group option requires a group id\n");} else if (nargs > NR_SVC_SUPP_GIDS + 2) {parse_error(state, "group option accepts at most %d supp. groups\n",NR_SVC_SUPP_GIDS);} else {int n;svc->gid = decode_uid(args[1]);for (n = 2; n < nargs; n++) {svc->supp_gids[n-2] = decode_uid(args[n]);}svc->nr_supp_gids = n - 2;}break;case K_keycodes:if (nargs < 2) {parse_error(state, "keycodes option requires atleast one keycode\n");} else {svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));if (!svc->keycodes) {parse_error(state, "could not allocate keycodes\n");} else {svc->nkeycodes = nargs - 1;for (i = 1; i < nargs; i++) {svc->keycodes[i - 1] = atoi(args[i]);}}}break;case K_oneshot:svc->flags |= SVC_ONESHOT;break;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;case K_critical:svc->flags |= SVC_CRITICAL;break;case K_setenv: { /* name value */struct svcenvinfo *ei;if (nargs < 2) {parse_error(state, "setenv option requires name and value arguments\n");break;}ei = calloc(1, sizeof(*ei));if (!ei) {parse_error(state, "out of memory\n");break;}ei->name = args[1];ei->value = args[2];ei->next = svc->envvars;svc->envvars = ei;break;}case K_socket: {/* name type perm [ uid gid ] */struct socketinfo *si;if (nargs < 4) {parse_error(state, "socket option requires name, type, perm arguments\n");break;}if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")&& strcmp(args[2],"seqpacket")) {parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");break;}si = calloc(1, sizeof(*si));if (!si) {parse_error(state, "out of memory\n");break;}si->name = args[1];si->type = args[2];si->perm = strtoul(args[3], 0, 8);if (nargs > 4)si->uid = decode_uid(args[4]);if (nargs > 5)si->gid = decode_uid(args[5]);si->next = svc->sockets;svc->sockets = si;break;}case K_user:if (nargs != 2) {parse_error(state, "user option requires a user id\n");} else {svc->uid = decode_uid(args[1]);}break;case K_seclabel: #ifdef HAVE_SELINUXif (nargs != 2) {parse_error(state, "seclabel option requires a label string\n");} else {svc->seclabel = args[1];} #endifbreak;default:parse_error(state, "invalid option '%s'\n", args[0]);} }該函數(shù)就是對(duì)Service的Option進(jìn)行解析,并把相應(yīng)的struct service的字段賦值。
Services的Option是服務(wù)的修飾符,可以影響服務(wù)如何以及怎樣運(yùn)行。服務(wù)支持的選項(xiàng)如下:
1. critical表明這是一個(gè)非常重要的服務(wù)。如果該服務(wù)4分鐘內(nèi)退出大于4次,系統(tǒng)將會(huì)重啟并進(jìn)入 Recovery (恢復(fù))模式。
2. disabled表明這個(gè)服務(wù)不會(huì)同與他同trigger (觸發(fā)器)下的服務(wù)自動(dòng)啟動(dòng)。該服務(wù)必須被明確的按名啟動(dòng)。必須通過start <service name>Command,class_start <class_name>Command不能啟動(dòng)即使該服務(wù)在
3. setenv <name> <value>在進(jìn)程啟動(dòng)時(shí)將環(huán)境變量設(shè)置為。
4. socket <name> <type> <perm> [ <user> [ <group> ] ]創(chuàng)建一個(gè)unix域的名為/dev/socket/?的套接字,并傳遞它的文件描述符給已啟動(dòng)的進(jìn)程。?必須是 "dgram","stream" 或"seqpacket"。用戶和組默認(rèn)是0。
5. user <username>在啟動(dòng)這個(gè)服務(wù)前改變?cè)摲?wù)的用戶名。此時(shí)默認(rèn)為 root。
6. group <groupname> [<groupname> ]*在啟動(dòng)這個(gè)服務(wù)前改變?cè)摲?wù)的組名。除了(必需的)第一個(gè)組名,附加的組名通常被用于設(shè)置進(jìn)程的補(bǔ)充組(通過setgroups函數(shù)),檔案默認(rèn)是root。
7. oneshot服務(wù)退出時(shí)不重啟。
8. class <name>指定一個(gè)服務(wù)類。所有同一類的服務(wù)可以同時(shí)啟動(dòng)和停止。如果不通過class選項(xiàng)指定一個(gè)類,則默認(rèn)為"default"類服務(wù)。
9. onrestart當(dāng)服務(wù)重啟,執(zhí)行一個(gè)命令。
Section Action
每個(gè)Action以及包含的Command由如下數(shù)據(jù)結(jié)構(gòu)表示:
struct command {/* list of commands in an action */struct listnode clist;int (*func)(int nargs, char **args);int nargs;char *args[1]; };struct action {/* node in list of all actions */struct listnode alist;//連接到action_list/* node in the queue of pending actions */struct listnode qlist;//連接到action_queue/* node in list of actions for a trigger */struct listnode tlist;unsigned hash;const char *name;struct listnode commands;//包含command的鏈表表頭struct command *current; };函數(shù)parse_action(/system/core/init/init_parser.c)
static void *parse_action(struct parse_state *state, int nargs, char **args) {struct action *act;if (nargs < 2) {parse_error(state, "actions must have a trigger\n");return 0;}if (nargs > 2) {parse_error(state, "actions may not have extra parameters\n");return 0;}act = calloc(1, sizeof(*act));act->name = args[1];list_init(&act->commands);list_add_tail(&action_list, &act->alist);/* XXX add to hash */return act; }首先本Section的第一行必須是如下格式
on <trigger name>最后把Action掛到action_list尾部。可以看到似乎打算把Action加到hash表中但是還沒有實(shí)現(xiàn)。
下面分析parse_line_action函數(shù)
static void parse_line_action(struct parse_state* state, int nargs, char **args) {struct command *cmd;struct action *act = state->context;int (*func)(int nargs, char **args);int kw, n;if (nargs == 0) {return;}kw = lookup_keyword(args[0]);if (!kw_is(kw, COMMAND)) {parse_error(state, "invalid command '%s'\n", args[0]);return;}n = kw_nargs(kw);if (nargs < n) {parse_error(state, "%s requires %d %s\n", args[0], n - 1,n > 2 ? "arguments" : "argument");return;}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(&act->commands, &cmd->clist); }Actions后需要跟若干個(gè)命令,這些命令如下:
1. exec <path> [<argument> ]*創(chuàng)建和執(zhí)行一個(gè)程序(<path>)。在程序完全執(zhí)行前,init將會(huì)阻塞。由于它不是內(nèi)置命令,應(yīng)盡量避免使用exec ,它可能會(huì)引起init執(zhí)行超時(shí)。
2. export <name> <value>在全局環(huán)境中將<name>變量的值設(shè)為<value>。(這將會(huì)被所有在這命令之后運(yùn)行的進(jìn)程所繼承)
3. ifup <interface>啟動(dòng)網(wǎng)絡(luò)接口
4. import <filename>指定要解析的其他配置文件。常被用于當(dāng)前配置文件的擴(kuò)展
5. hostname <name>設(shè)置主機(jī)名
6. chdir <directory>改變工作目錄
7. chmod <octal-mode><path>改變文件的訪問權(quán)限
8. chown <owner><group> <path>更改文件的所有者和組
9. chroot <directory>改變處理根目錄
10. class_start<serviceclass>啟動(dòng)所有指定服務(wù)類下的未運(yùn)行服務(wù)。
11 class_stop<serviceclass>停止指定服務(wù)類下的所有已運(yùn)行的服務(wù)。
12. domainname <name>設(shè)置域名
13. insmod <path>加載path指定的驅(qū)動(dòng)模塊
14. mkdir <path> [mode][owner] [group]創(chuàng)建一個(gè)目錄<path>?,可以選擇性地指定mode、owner以及group。如果沒有指定,默認(rèn)的權(quán)限為755,并屬于root用戶和 root組。
15. mount <type> <device> <dir> [<mountoption> ]*試圖在目錄<dir>掛載指定的設(shè)備。<device>?可以是mtd@name的形式指定一個(gè)mtd塊設(shè)備。<mountoption>包括 "ro"、"rw"、"re
16. setkey保留,暫時(shí)未用
17. setprop <name><value>將系統(tǒng)屬性<name>的值設(shè)為<value>。
18. setrlimit <resource> <cur> <max>設(shè)置<resource>的rlimit (資源限制)
19. start <service>啟動(dòng)指定服務(wù)(如果此服務(wù)還未運(yùn)行)。
20.stop<service>停止指定服務(wù)(如果此服務(wù)在運(yùn)行中)。
21. symlink <target> <path>創(chuàng)建一個(gè)指向<path>的軟連接<target>。
22. sysclktz <mins_west_of_gmt>設(shè)置系統(tǒng)時(shí)鐘基準(zhǔn)(0代表時(shí)鐘滴答以格林威治平均時(shí)(GMT)為準(zhǔn))
23. trigger <event>觸發(fā)一個(gè)事件。用于Action排隊(duì)
24. wait <path> [<timeout> ]等待一個(gè)文件是否存在,當(dāng)文件存在時(shí)立即返回,或到指定的超時(shí)時(shí)間后返回,如果不指定,默認(rèn)超時(shí)時(shí)間是5秒。
25. write <path> <string> [ <string> ]*向<path>指定的文件寫入一個(gè)或多個(gè)字符串。
Section Import
每個(gè)Import由如下數(shù)據(jù)結(jié)構(gòu)表示:
struct import {struct listnode list;const char *filename; };void parse_import(struct parse_state *state, int nargs, char **args) {struct listnode *import_list = state->priv;struct import *import;char conf_file[PATH_MAX];int ret;if (nargs != 2) {ERROR("single argument needed for import\n");return;}ret = expand_props(conf_file, args[1], sizeof(conf_file));if (ret) {ERROR("error while handling import on line '%d' in '%s'\n",state->line, state->filename);return;}import = calloc(1, sizeof(struct import));import->filename = strdup(conf_file);list_add_tail(import_list, &import->list);INFO("found import '%s', adding to import list", import->filename); }函數(shù)expand_props把配置文件中的${<property_name>},通過Property System讀取對(duì)應(yīng)的值。通常要讀取ro.machine和ro.arch的值,展開后形成真正的文件名,然后掛載在state.priv上。
在parse_config函數(shù)的末尾,有如下代碼:
parser_done:list_for_each(node, &import_list) {struct import *import = node_to_item(node, struct import, list);int ret;INFO("importing '%s'", import->filename);ret = init_parse_config_file(import->filename);實(shí)現(xiàn)了對(duì)import進(jìn)的文件的解析。
總結(jié)
以上是生活随笔為你收集整理的Android init.rc分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 链表的分类
- 下一篇: Android init第三、四部分详细