1. 程式人生 > >Android init原始碼分析(2)init.rc解析

Android init原始碼分析(2)init.rc解析

    action_for_each_trigger("early-init", action_add_queue_tail);

    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_lin一部分在ux_rng");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");

    /* execute all the boot actions to get us started */
    action_for_each_trigger("init", action_add_queue_tail);

    /* skip mounting filesystems in charger mode */
    if (!is_charger) {
        action_for_each_trigger("early-fs", action_add_queue_tail);
        action_for_each_trigger("fs", action_add_queue_tail);
        action_for_each_trigger("post-fs", action_add_queue_tail);
        action_for_each_trigger("post-fs-data", action_add_queue_tail);
    }

    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     * wasn't ready immediately after wait_for_coldboot_done
     */
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(signal_init_action, "signal_init");
    queue_builtin_action(check_startup_action, "check_startup");

    if (is_charger) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("early-boot", action_add_queue_tail);
        action_for_each_trigger("boot", action_add_queue_tail);
    }

        /* run all property triggers based on current state of the properties */
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");


#if BOOTCHART
    queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif

void action_for_each_trigger(const char *trigger, void (*func)(struct action *act))
該函式利用字串trigger從從action_list中查詢特定Actions,為每個符合條件的Actions執行action_add_queue_tail函式,也就是說將action_list中所有觸發器為trigger的Actions新增到action_queue中。
void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
該函式完成兩個操作:
1)構造一個觸發器為name的struct action結構體,並建立一個struct command,對應函式為行參func
2)將struct action新增到action_queue連結串列末尾。

從名字上來看action_queue就是Actions佇列,而佇列具有FIFO特性。事實也是如此,action_queue可以用於控制Actions執行的順序。
上述程式碼結束後,action_queue連結串列最終具有如下形式,每個連結串列都是struct action資料結構,各節點通過struct action中的qlist變數彼此連線。 AndroidInit3.png

白色節點來自init啟動指令碼,橘紅色節點則有函式queue_builtin_action建立。

現在萬事具備,只欠東風。接下來就是如何執行這些Actions了。
init.c main
    for(;;) {
        execute_one_command();
        ...
    }

execute_one_command函式分析
概括的說,這個函式每次從action_queue開頭取出一個Actions,同時移除對應節點,依次執行每個command。由與main函式中for迴圈語句的存在,最終所有的action_queue中所有Actions都會被執行。大致執行流程如此。

簡要總結下:init程序的邏輯時,任何一個Actions想要被執行,只要把自己放到action_queue中,然後在init程序的for迴圈中就會在之後的某個時間被執行。 細心的讀者已經發現,為什麼action_queue中的Actions如此之少(請參考本文前面介紹Actions時),至少“post-fs”、“post-fs-data”、“boot”,以及"property:<name>=<value>”等Actions怎麼沒有?

這是因為init程序耍了花招,它把這部分Actions的觸發放到了init.rc裡,由Actions的一條特殊command: "trigger"觸發。請注意,該trigger與Actions的名字(也稱為trigger)不同。 見init.rc
on late-init
    trigger early-fs
    trigger fs
    trigger post-fs
    trigger post-fs-data

    # Load properties from /system/ + /factory after fs mount. Place
    # this in another action so that the load will be scheduled after the prior
    # issued fs triggers have completed.
    trigger load_all_props_action

    # Remove a file to wake up anything waiting for firmware.
    trigger firmware_mounts_complete

    trigger early-boot
    trigger boot

看到這裡,先大膽猜測當init程序執行到“late-init” Actions時,對於每條trigger語句,取出後面所跟的字串,從action_list(“媽蛋,終於想起我來了”)中找出該Actions對應的struct action結構,新增到action_queue中。快去看看程式碼確定一下是否如此。怎麼找到command對應程式碼?前面提到init啟動指令碼所有關鍵字都keyword.h中,
//keyword.h
//-------------
    KEYWORD(trigger,     COMMAND, 1, do_trigger)
//builtins.c
//-------------
int do_trigger(int nargs, char **args)
{
    action_for_each_trigger(args[1], action_add_queue_tail);
    return 0;
}

bingo!原來trigger命令對應的函式do_trigger中再次呼叫了action_for_each_trigger,可見之前的猜測正確!
至於"property:<name>=<value>”型別的Actions由於需要在屬性條件滿足時被執行,在android4.4的init的實現中對這類Actions的處理比較特殊,有兩種方式:
(1) 通過action_queue,配合execute_one_command處理
參考上面action_queue連結串列圖,在main函式中使用queue_builtin_action建立了一個特殊的Actions:"queue_property_triggers",該Actions內只有一個command,對應函式為queue_property_triggers_action,程式碼如下:
static int queue_property_triggers_action(int nargs, char **args)
{
    queue_all_property_triggers();
    /* enable property triggers */
    property_triggers_enabled = 1;
    return 0;
}
void queue_all_property_triggers()
{
    struct listnode *node;
    struct action *act;
    list_for_each(node, &action_list) {
        act = node_to_item(node, struct action, alist);
        if (!strncmp(act->name, "property:", strlen("property:"))) {
            /* parse property name and value
               syntax is property:<name>=<value> */
            const char* name = act->name + strlen("property:");
            const char* equals = strchr(name, '=');
            if (equals) {
                char prop_name[PROP_NAME_MAX + 1];
                char value[PROP_VALUE_MAX];新浪微博
                int length = equals - name;
                if (length > PROP_NAME_MAX) {
                    ERROR("property name too long in trigger %s", act->name);
                } else {
                    int ret;
                    memcpy(prop_name, name, length);
                    prop_name[length] = 0;

                    /* does the property exist, and match the trigger value? */
                    ret = property_get(prop_name, value);
                    if (ret > 0 && (!strcmp(equals + 1, value) ||
                                    !strcmp(equals + 1, "*"))) {
                        action_add_queue_tail(act);
                    }
                }
            }
        }
    }
}
可見queue_all_property_triggers函式中會檢查從action_list中查詢所有property型的Actions,並判斷其屬性條件是否滿足,若滿足則使用action_add_queue_tail(act)將其新增到action_queue中。

仔細考慮一下,這種方式並不能保證所有property型的Actions都能在屬性滿足後被觸發執行,參考action_queue連結串列圖,這種方式僅僅能保證在"queue_property_triggers"之前的Actions執行中所設定的屬性。設想這種情況,在init.rc新增某個Actions,希望可以在Android命令列終端中設定屬性(android的set_property命令可以設定android屬性),從而觸發某個動作。僅僅通過action_queue配合execute_one_command是難以優雅實現的,所以init程序使用了第二種方式。
思考:非優雅的實現最簡單方式,只要在for迴圈中定時輪詢屬性,但是延時間隔多少合適?若是間隔太短,則將會相當浪費CPU,若是間隔太長,則會導致設定某個屬性到執行其對應Actions的時間過長。優雅的方式可以利用程序見通訊機制避免CPU被浪費,並能保證響應的及時性。

(2) 藉助程序間通訊方式實現
首先細化init.c main函式中for迴圈程式碼,大致如下:
int main(int argc, char* argv[])
{
    ....
    for(;;) {
        execute_one_command();
        ....
        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)
            continue;
        for (i = 0; i < fd_count; i++) {
            if (ufds[i].revents == POLLIN) {這部分內容請閱讀
                if (ufds[i].fd == get_property_set_fd())
                    handle_property_set_fd();
                ...
            }
        }
    }
}

當某個程式(程序)呼叫property_set(libcutils庫)來自設定屬性,就會給init程序傳送一個訊息(通過Unix domain socket),最終init接收到整個訊息後,會呼叫
handle_property_set_fd() -> property_set(來自init/property_service.c)->  property_changed() -> queue_property_triggers()->action_add_queue_tail
在queue_property_triggers函式中從全域性action_list中匹配 property:<name>=<value>型別的Actions,如果屬性數值滿足,則將該Actions加入到action-queue中,這樣該Actions中的command將會在之後的execute_one_command()被呼叫。

在本文中涉及很多Android屬性的操作,筆者將在文章《Android init原始碼分析(3)屬性系統》進一步分析Android的屬性系統。 此外還有些問題需要解決: (1). service將如何執行,並且service存在多種屬性,當包含restart屬性時,init程序是如何重啟的? (2). for迴圈中execute_one_command之後的大量程式碼的含義?

第一個問題將在文章《Android init原始碼分析(4)service的處理》中具體分析。至於for迴圈中其他程式碼,將分散在init原始碼分析(3)與(4)這兩篇文章中。

本文由prife原創,轉載請註明出處。部落格地址:blog.csdn.net/prife ,豆瓣prife,weibo prife

相關推薦

Android init原始碼分析2init.rc解析

action_for_each_trigger("early-init", action_add_queue_tail); queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); q

Android進階3:Activity原始碼分析2 —— Activity啟動和銷燬流程8.0

上篇文章講述了app從啟動建立Activity呼叫onCreate,onStart, onResume方法,這篇文章講述一下Activity啟動的另一個切入點:startActivity方法,啟動Activity。 通過上一篇文章,我們總結一下: 1:A

Android ADB 原始碼分析

前言 之前分析的兩篇文章 Android Adb 原始碼分析(一) 嵌入式Linux:Android root破解原理(二)   寫完之後,都沒有寫到相關的實現程式碼,這篇文章寫下ADB的通訊流程的一些細節 看這篇文章之前,請先閱讀 Linux的SOCKET

Mybatis 原始碼分析2—— 引數處理

Mybatis對引數的處理是值得推敲的,不然在使用的過程中對發生的一系列錯誤直接懵逼了。 以前遇到引數繫結相關的錯誤我就是直接給加@param註解,也稀裡糊塗地解決了,但是後來遇到了一些問題推翻了我的假設:單個引數不需要使用 @param 。由此產生了一個疑問,Mybatis到底是怎

PackageManagerService 原始碼分析2

  一.scanPackageLI PKMS 中呼叫scanDirLI來分析APK 檔案,如果目錄下的是apk檔案或者是目錄,會繼續呼叫scanPackageLI函式: private PackageParser.Package scanPackageLI(File s

tensorflowV1.11-原始碼分析2

通過前面的run_shell函式,執行python指令碼,並返回python庫的路徑。 def get_python_path(environ_cp, python_bin_path): """Get the python site package paths.""" python_paths = [

Django rest framework原始碼分析2----許可權

目錄 新增許可權 (1)API/utils資料夾下新建premission.py檔案,程式碼如下: message是當沒有許可權時,提示的資訊 # utils/permission.py class SVIPPremission(object): message =

Android recyclerview原始碼分析

原始碼分析基於22.2.1版本 先預覽一下recyclerview 相關的類   今天先分析SortedList 和SortedListAdapterCallback 先看下這兩個類的用法  SortedList<Object> mDataList=new

【原創】docker原始碼分析2---docker server

上一節,分析了Engine和job。那這一節就開始講下docker server。 1、docker server 1.1 主體流程 我們從main函式開始,看看docker server

Android SharedPreference 原始碼分析

1. 前言 眾所周知,SharedPreferences是Android平臺上一個輕量級的儲存類,用來儲存應用的一些常用配置,比如Activity狀態,Activity暫停時,將此activity的狀態儲存到SharedPereferences中;當Activ

HDFS原始碼分析2----HDFS原始碼結構

BlockPlacementPolicy.java----抽象類:這個介面用於選擇放置塊副本的目標磁碟的所需的數目; BlockPlacementPolicyDefault.java----繼承實現類:這個類實現了選擇放置塊副本的目標磁碟的所需的數目; BlockPlacementPolicyWithNode

Hadoop原始碼分析2————MapReduce之MapTask

MapTask(Hadoop2.7.3) MapTask.java繼承於Task,是hadoop中Map節點主要所做的主要流程。 一般被jvmtask初始化或者在MapTaskAttemptImpl被初始化。其主要流程寫在run()方法中。 run()方法

U-Boot啟動過程原始碼分析2-第二階段

先總述:第一階段cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S進行初始化,再跳到第二階段的入口點lib_arm/board.c中的start_armboot函式。 第二階段start_armboot函式需

libevent原始碼分析2--2.1.8--結構體 struct event和struct event_callback

一、event_callback結構體 struct event_callback { //下一個回撥事件 TAILQ_ENTRY(event_callback) evcb_active_next; //回撥事件的狀態標識,具體為:

opendaylightLi l2switch 原始碼分析2--parent

本文主要介紹l2switch中的parent工程,該工程定義了執行L2switch所使用的依賴模組以及版本等。 該工程下只有一個pom.xml檔案,下面對該檔案中的主要內容進行說明: 1. <parent>     <groupId>org.open

MySQL原始碼分析2:Mysql中的記憶體分配相關

Mysql中的記憶體分配相關 涉及到記憶體的配置引數這些引數可以分成兩部分,分別對應MySQL中的兩個層次:伺服器層和儲存引擎層。 MySQL伺服器相關: 每個連線到MySQL伺服器的執行緒都需要有自己的緩衝,預設為其分配256K。事務開始之後,則需要增加更多的空間。執行較

webpack原始碼分析2---- webpack\bin\webpack.js

前言 上文講到呼叫webpack指令實際執行的是node webpack\bin\webpack.js這段程式碼,我們今天的目的就

Mybatis原始碼分析1—— Mapper檔案解析

感覺CSDN對markdown的支援不夠友好,總是伴隨各種問題,很惱火! xxMapper.xml的解析主要由XMLMapperBuilder類完成,parse方法來完成解析: public void parse() { if (!configuration.isRes

RecyclerView 原始碼分析 —— 繪製流程解析

概述 對於 RecyclerView 是那麼熟悉又那麼陌生。熟悉是因為作為一名 Android 開發者,RecyclerView 是經常會在專案裡面用到的,陌生是因為只是知道怎麼用,但是卻不知道 RecyclerView 的內部實現機制。 但凡是一位有所追求的開發者,都不會只讓自己停留在只會使用上,

Android 消息處理源代碼分析2

urn msg illegal r.java roi fin 報錯 mes pri Android 消息處理源代碼分析(1)點擊打開鏈接 繼續接著分析剩下的類文件 Looper.java public final class Looper {