Android init.rc文件詳解

首先,個人爲了很好的總結這方面的知識體系,以下絕大多數內容都是博主從網上那些大神多方面摘抄過來的。

一、文件簡介

init.rc:Android在啓動過程中讀取的啓動腳本文件,主要完成一些初級的初始化,在/system/core/init/init.c中解析。rc 經常被用作程序之啓動腳本的文件名。它是“run commands”(運行命令)的縮寫。

init.xx.rc:與具體CPU相關的啓動腳本,比如對於MTK的CPU,名字爲init.usb.rc,init.trace.rc。在init.rc之後得到解析。

對於運行的設備,兩個文件都位於根目錄下:autochips\device\atc\ac8317\configs
對於編譯後的源碼,位於out\target\product\ac8317\root

二、init.rc和init.xx.rc文件的修改

我們以 init.rc 來入手,學習 rc 的用法。
\autochips\device\atc\ac8317\configs\init_for_mmc.rc
system/core/init.c

文件結構
init.rc 基本單位是 section。
section 有三種類型:
1. on
2. service
3. import
如果想對這方面作個更加詳細的理解的朋友請移步至Android init.rc文件淺析 閱讀

on 類型
on 類型 表示一系列的命令組合,eg:

on init
    mkdir /productinfo 0771 system system
    # SPRD: modify for storage manage @{
    mount tmpfs tmpfs /storage mode=0751,gid=1028
    # @}
    mkdir /mnt/media_rw/sdcard0 0700 media_rw media_rw
    mkdir /storage/sdcard0 0700 root root
    mkdir /mnt/shell/emulated 0700 shell shell
    mkdir /storage/emulated 0555 root root

    # SPRD: move this to board level init @{
    #export EXTERNAL_STORAGE /storage/emulated/legacy
    #export SECONDARY_STORAGE /storage/sdcard0
    # @}
    # SPRD: for storage manage @{
    export LEGACY_STORAGE /storage/emulated/legacy
    # @}

這樣一個 section 裏面包含了多個命令。命令的執行是以 section 爲單位的。
上面這些命令都會一起順序執行,不會單獨執行。
他們的執行由 init.c 的 main() 決定。在當其中調用

init.c文件:

action_for_each_trigger("early-init", action_add_queue_tail);

時,就將 on init 開始的這樣一個 section 裏所有的命令加入到一個執行隊列,在未來某個時候會順序執行隊列裏的命令。

該方法在init_parser.c文件中實現:

void action_for_each_trigger(const char *trigger,
                             void (*func)(struct action *act))
{
    struct listnode *node;
    struct action *act;
    list_for_each(node, &action_list) {
        act = node_to_item(node, struct action, alist);
        if (!strcmp(act->name, trigger)) {
            func(act);
        }
    }
}

這裏寫圖片描述

service 類型
service 類型 的section 表示一個可執行的程序,下面是個人工作內容中片段代碼:

例一:

service gocsdk /system/bin/gocsdk
    class main
    disabled
    oneshot

on property:sys.boot_completed=1
    chmod 777 /dev/goc_serial
    start gocsdk

gocsdk 作爲一個名字標識了這個 service,這個可執行程序的位置在 /system/bin/gocsdk。
下面的 disable、oneshot 被稱爲 options,options 是用來描述的 service 的特點,不同的 service 有不同的 options。

service 的執行時間是在property:sys.boot_completed=1 這個命令被執行的時候,這個命令總存在於某個 on 類型的section 中作爲一個服務啓動的動作(Action)。

就拿上面的代碼來說property:sys.boot_completed=1滿足條件被執行時,則會啓動這個類型爲 main的 gocsdk service。

例二:

service yo_service1 /system/bin/yo_service1
    class core
    user system
    disabled
    group system radio shell
    oneshot

on yo_fs
    class_start core

其中 yo_service1 這個 service 的類型是 core。
在 yo_fs 被調用的時候則將會 class_start 而執行所有 類型爲 core 的 service。

import 類型
import 類型表示包含了另外一些 section,在解析完 init.rc 後會繼續調用 init_parse_config_file 來解析引入的 .rc 文件。
eg:
比如我們在 init.sc8830.rc 的開始可以看到

import /init.usb.rc
import /init.trace.rc

表示在運行完本 rc 後還將繼續運行 init.usb.rc 和 init.trace.rc。

init.rc 文件解析過程
解析 init.rc 的過程就是識別一個個 section 的過程。
在 init.c 中的 main() 中去執行一個個命令。
(android採用雙向鏈表來存儲section的信息,解析完成之後,會得到三個雙向鏈表action_list、service_list、import_list來分別存儲三種section的信息上。)

system/core/init/init.c
在 init.c 中調用

init_parse_config_file("/init.rc");

其代碼實現如下:

int init_parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);        //read_file()調用open\lseek\read 將init.rc讀出來
    if (!data) return -1;

    parse_config(fn, data);        //調用parse_config開始解析
    DUMP();
    return 0;
}

parse_config() 代碼:

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];
    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)) {                   //next_token()根據從state.ptr開始遍歷
        case T_EOF:                                     //遍歷到文件結尾,然後goto解析import的.rc文件
            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)) {                        //如果這是一個section的第一行                                            
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {                                         //如果這不是一個section的第一行
                    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);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}

next_token() 解析完 init.rc 中一行之後,
會返回T_NEWLINE,這時調用 lookup_keyword 函數來找出這一行的關鍵字, lookup_keyword返回的是一個整型值,對應keyword_info[]數組的下標,keyword_info[]存放的是keyword_info結構體類型的數據,

struct {
    const char *name;                      //關鍵字的名稱
    int (*func)(int nargs, char **args);   //對應的處理函數
    unsigned char nargs;                   //參數個數
    unsigned char flags;                   //flag標識關鍵字的類型,包括COMMAND、OPTION、SECTION
} keyword_info

因此keyword_info[]中存放的是所有關鍵字的信息,每一項對應一個關鍵字。
根據每一項的flags就可以判斷出關鍵字的類型,如新的一行是SECTION,就調用parse_new_section()來解析這一行,
如新的一行不是一個SECTION的第一行,那麼調用state.parseline()來解析(state.parseline所對應的函數會根據section類型的不同而不同),在parse_new_section()中進行動態設置。

三種類型的section: service、on、import,
service對應的state.parseline爲parse_line_service,
on對應的state.parseline爲parse_line_action,
import section中只有一行所以沒有對應的state.parseline。

發佈了70 篇原創文章 · 獲贊 68 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章