(連載)Android 8.0 : 系統啓動流程之init進程(三)

這是一個連載的博文系列,我將持續爲你們提供儘量透徹的Android源碼分析 github連載地址html

前言

init通過前兩個階段後,已經創建了屬性系統和SELinux系統,可是init進程還須要執行不少其餘的操做,還要啓動許多關鍵的系統服務, 可是若是都是像屬性系統和SELinux系統那樣一行行代碼去作,顯得有點雜亂繁瑣,並且不容易擴展,因此Android系統引入了init.rcjava

init.rc是init進程啓動的配置腳本,這個腳本是用一種叫Android Init Language(Android初始化語言)的語言寫的, 在7.0之前,init進程只解析根目錄下的init.rc文件,可是隨着版本的迭代,init.rc愈來愈臃腫, 因此在7.0之後,init.rc一些業務被分拆到/system/etc/init,/vendor/etc/init,/odm/etc/init三個目錄下, 在本篇文章中,我將講解init.rc的一些語法,而後一步步分析init進程是如何去解析init.rc文件的linux

本文主要講解如下內容android

  • Android Init Language語法
  • 解析.rc文件
  • 加入一些事件和一些Action
  • 觸發全部事件並不斷監聽新的事件

本文涉及到的文件ios

platform/system/core/init/README.md
platform/system/core/init/init.cpp
platform/system/core/init/init_parser.cpp
platform/system/core/init/action.cpp
platform/system/core/init/action.h
platform/system/core/init/keyword_map.h
platform/system/core/init/builtins.cpp
platform/system/core/init/service.cpp
platform/system/core/init/service.h
platform/system/core/init/import_parser.cpp
platform/system/core/init/util.cpp
複製代碼

1、Android Init Language語法

定義在platform/system/core/init/README.mdc++

.rc文件主要配置了兩個東西,一個是action,一個是service,trigger和command是對action的補充,options是對service的補充. action加上trigger以及一些command,組成一個Section,service加上一些option,也組成一個Section ,.rc文件就是由一個個Section組成. .rc文件頭部有一個import的語法,表示這些.rc也一併包含並解析,接下來咱們重點講下action和service.git

action的格式以下:github

on <trigger> [&& <trigger>]*
       <command>
       <command>
       <command>
複製代碼

以on開頭,trigger是判斷條件,command是具體執行一些操做,當知足trigger條件時,執行這些command
trigger能夠是一個字符串,如數組

on early //表示當trigger early或QueueEventTrigger("early")調用時觸發
複製代碼

也能夠是屬性,如安全

on property:sys.boot_from_charger_mode=1//表示當sys.boot_from_charger_mode的值經過property_set設置爲1時觸發
on property:sys.sysctl.tcp_def_init_rwnd=* // *表示任意值

複製代碼

條件能夠是多個,用&&鏈接,如

on zygote-start && property:ro.crypto.state=unencrypted
//表示當zygote-start觸發而且ro.crypto.state屬性值爲unencrypted時觸發
複製代碼

command就是一些具體的操做,如

mkdir /dev/fscklogs 0770 root system //新建目錄
class_stop charger //終止服務
trigger late-init  //觸發late-init
複製代碼

services的格式以下:

service <name> <pathname> [ <argument> ]*
       <option>
       <option>
       ...
複製代碼

以service開頭,name是指定這個服務的名稱,pathname表示這個服務的執行文件路徑,argument表示執行文件帶的參數,option表示這個服務的一些配置 咱們看一個典型的例子就知道了

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks
複製代碼

這個是配置在 /init.zygote64_32.rc文件中的service, 它就是咱們常說的zygote進程的啓動配置

zygote是進程名,可執行文件路徑在/system/bin/app_process64,執行文件參數(就是可執行程序main函數裏面的那個args)是 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote

後面的option是一些服務配置,好比 class main表示所屬class是main,至關於一個歸類,其餘service也能夠歸爲main,他們會被一塊兒啓動或終止, service有一個name,也有一個class,就像工做中,你有一個名字叫foxleezh,也能夠說你屬於android部門.

我上面說的這些東西,源碼中已經有一個專門的文檔用來講明,路徑在platform/system/core/init/README.md,應當說這個文檔寫得仍是挺不錯的,認真讀這個文檔的話,基本的語法知識就都知道了,我簡單翻譯下

Android Init Language

Android Init Language中由5類語法組成,分別是Actions, Commands, Services, Options, and Imports

每一行是一個語句,單詞之間用空格分開,若是單詞中有空格能夠用反斜槓轉義,也能夠用雙引號來引用文本避免和空格衝突,若是一行語句太長能夠用 \ 換行,用 # 表示註釋

Actions和Services能夠做爲一個獨立的Section,全部的Commands和Options從屬於緊挨着的Actions或Services,定義在第一個Section前的Commands和Options將被忽略掉

Actions和Services都是惟一的,若是定義了兩個同樣的Action,第二個Action的Command將追加到第一個Action, 若是定義了兩個同樣的Service,第二個Service將被忽略掉並打印錯誤日誌

Init .rc Files

Android Init Language是用後綴爲.rc的純文本編寫的,並且是由多個分佈在不一樣目錄下的.rc文件組成,以下所述

/init.rc 是最主要的一個.rc文件,它由init進程在初始化時加載,主要負責系統初始化,它會導入 /init.${ro.hardware}.rc ,這個是系統級核心廠商提供的主要.rc文件

當執行 mount_all 語句時,init進程將加載全部在 /{system,vendor,odm}/etc/init/ 目錄下的文件,掛載好文件系統後,這些目錄將會爲Actions和Services服務

有一個特殊的目錄可能被用來替換上面的三個默認目錄,這主要是爲了支持工廠模式和其餘非標準的啓動模式,上面三個目錄用於正常的啓動過程

這三個用於擴展的目錄是

  1. /system/etc/init/ 用於系統自己,好比SurfaceFlinger, MediaService, and logcatd.
  2. /vendor/etc/init/ 用於SoC(系統級核心廠商,如高通),爲他們提供一些核心功能和服務
  3. /odm/etc/init/ 用於設備製造商(odm定製廠商,如華爲、小米),爲他們的傳感器或外圍設備提供一些核心功能和服務

全部放在這三個目錄下的Services二進制文件都必須有一個對應的.rc文件放在該目錄下,而且要在.rc文件中定義service結構, 有一個宏LOCAL_INIT_RC,能夠幫助開發者處理這個問題. 每一個.rc文件還應當包含一些與該服務相關的actions

舉個例子,在system/core/logcat目錄下有logcatd.rc和Android.mk這兩個文件. Android.mk文件中用LOCAL_INIT_RC這個宏,在編譯時將logcatd.rc放在/system/etc/init/目錄下,init進程在調用 mount_all 時將其加載,在合適的時機運行其定義的service並將action放入隊列

將init.rc根據不一樣服務分拆到不一樣目錄,要比以前放在單個init.rc文件好. 這種方案確保init讀取的service和action信息能和同目錄下的Services二進制文件更加符合,再也不像之前單個init.rc那樣. 另外,這樣還能夠解決多個services加入到系統時發生的衝突,由於他們都拆分到了不一樣的文件中

在 mount_all 語句中有 "early" 和 "late" 兩個可選項,當 early 設置的時候,init進程將跳過被 latemount 標記的掛載操做,並觸發fs encryption state 事件, 當 late 被設置的時候,init進程只會執行 latemount 標記的掛載操做,可是會跳過導入的 .rc文件的執行. 默認狀況下,不設置任何選項,init進程將執行全部掛載操做

Actions

Actions由一行行命令組成. trigger用來決定何時觸發這些命令,當一個事件知足trigger的觸發條件時, 這個action就會被加入處處理隊列中(除非隊列中已經存在)

隊列中的action按順序取出執行,action中的命令按順序執行. 這些命令主要用來執行一些操做(設備建立/銷燬,屬性設置,進程重啓)

Actions的格式以下:

on <trigger> [&& <trigger>]*
       <command>
       <command>
       <command>
複製代碼

Services

Services是init進程啓動的程序,它們也可能在退出時自動重啓. Services的格式以下:

service <name> <pathname> [ <argument> ]*
       <option>
       <option>
       ...
複製代碼

Options

Options是Services的參數配置. 它們影響Service如何運行及運行時機

console [<console>]
Service須要控制檯. 第二個參數console的意思是能夠設置你想要的控制檯類型,默認控制檯是/dev/console ,/dev 這個前綴一般是被忽略的,好比你要設置控制檯 /dev/tty0 ,那麼只須要設置爲console tty0

critical
表示Service是嚴格模式. 若是這個Service在4分鐘內退出超過4次,那麼設備將重啓進入recovery模式

disabled
表示Service不能以class的形式啓動,只能以name的形式啓動

setenv <name> <value>
在Service啓動時設置name-value的環境變量

socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
建立一個unix域的socket,名字叫/dev/socket/name , 並將fd返回給Service. type 只能是 "dgram", "stream" or "seqpacket". User 和 group 默認值是 0. 'seclabel' 是這個socket的SELinux安全上下文,它的默認值是service安全策略或者基於其可執行文件的安全上下文. 它對應的本地實如今libcutils的android_get_control_socket

file <path> <type>
打開一個文件,並將fd返回給這個Service. type 只能是 "r", "w" or "rw". 它對應的本地實如今libcutils的android_get_control_file

user <username>
在啓動Service前將user改成username,默認啓動時user爲root(或許默認是無). 在Android M版本,若是一個進程想擁有Linux capabilities(至關於Android中的權限吧),也只能經過設置這個值. 之前,一個程序要想有Linux capabilities,必須先以root身份運行,而後再降級到所需的uid. 如今已經有一套新的機制取而代之,它經過fs_config容許廠商賦予特殊二進制文件Linux capabilities. 這套機制的說明文檔在source.android.com/devices/tec…. 當使用這套新的機制時,程序能夠經過user參數選擇本身所需的uid,而不須要以root權限運行. 在Android O版本, 程序能夠經過capabilities參數直接申請所需的能力,參見下面的capabilities說明

group <groupname> [ <groupname>\* ]
在啓動Service前將group改成第一個groupname,第一個groupname是必須有的, 默認值爲root(或許默認值是無),第二個groupname能夠不設置,用於追加組(經過setgroups).

capabilities <capability> [ <capability>\* ]
在啓動Service時將capabilities設置爲capability. 'capability' 不能是"CAP_" prefix, like "NET_ADMIN" or "SETPCAP". 參考 http://man7.org/linux/man-pages/man7/capabilities.7.html ,裏面有capability的說明.

seclabel <seclabel>
在啓動Service前將seclabel設置爲seclabel. 主要用於在rootfs上啓動的service,好比ueventd, adbd. 在系統分區上運行的service有本身的SELinux安全策略,若是不設置,默認使用init的安全策略.

oneshot
退出後再也不重啓

class <name> [ <name>\* ]
爲Service指定class名字. 同一個class名字的Service會被一塊兒啓動或退出,默認值是"default",第二個name能夠不設置,用於service組.

animation class
animation class 主要包含爲開機動畫或關機動畫服務的service. 它們很早被啓動,並且直到關機最後一步才退出. 它們不容許訪問/data 目錄,它們能夠檢查/data目錄,可是不能打開 /data 目錄,並且須要在 /data 不能用時也正常工做 .

onrestart
在Service重啓時執行命令.

writepid <file> [ <file>\* ]
當Service調用fork時將子進程的pid寫入到指定文件. 用於cgroup/cpuset的使用,當/dev/cpuset/下面沒有文件但ro.cpuset.default的值卻不爲空時, 將pid的值寫入到/dev/cpuset/cpuset_name/tasks文件中

priority <priority>
設置進程優先級. 在-20~19之間,默認值是0,能過setpriority實現

namespace <pid|mnt>
當fork這個service時,設置pid或mnt標記

oom_score_adjust <value>
設置子進程的 /proc/self/oom_score_adj 的值爲 value,在 -1000 ~ 1000之間.

Triggers

Triggers 是個字符串,當一些事件發生知足該條件時,一些actions就會被執行

Triggers分爲事件Trigger和屬性Trigger

事件Trigger由trigger 命令或QueueEventTrigger方法觸發.它的格式是個簡單的字符串,好比'boot' 或 'late-init'.

屬性Trigger是在屬性被設置或發生改變時觸發. 格式是'property:='或'property:=*',它會在init初始化設置屬性的時候觸發.

屬性Trigger定義的Action可能有多種觸發方式,可是事件Trigger定義的Action可能只有一種觸發方式

好比:
on boot && property:a=b 定義了action的觸發條件是,boot Trigger觸發,而且屬性a的值等於b

on property:a=b && property:c=d 這個定義有三種觸發方式:

  1. 在初始化時,屬性a=b,屬性c=d.
  2. 在屬性c=d的狀況下,屬性a被改成b.
  3. A在屬性a=b的狀況下,屬性c被改成d.

Commands

bootchart [start|stop]
啓動或終止bootcharting. 這個出如今init.rc文件中,可是隻有在/data/bootchart/enabled文件存在的時候纔有效,不然不能工做

chmod <octal-mode> <path>
修改文件讀寫權限

chown <owner> <group> <path>
修改文件全部者或所屬用戶組

class_start <serviceclass>
啓動全部以serviceclass命名的未啓動的service(service有一個name,也有個class, 這裏的serviceclass就是class,class_start和後面的start是兩種啓動方式,class_start是class形式啓動,start是name形式啓動)

class_stop <serviceclass>
終止全部以serviceclass命名的正在運行的service

class_reset <serviceclass>
終止全部以serviceclass命名的正在運行的service,可是不由用它們. 它們能夠稍後被class_start重啓

class_restart <serviceclass>
重啓全部以serviceclass命名的service

copy <src> <dst>
複製一個文件,與write類似,比較適合二進制或比較大的文件.
對於src,從連接文件、world-writable或group-writable複製是不容許的.
對於dst,若是目標文件不存在,則默認權限是0600,若是存在就覆蓋掉

domainname <name>
設置域名

enable <servicename>
將一個禁用的service設置爲可用. 若是這個service在運行,那麼就會重啓. 通常用在bootloader時設置屬性,而後啓動一個service,好比 on property:ro.boot.myfancyhardware=1 enable my_fancy_service_for_my_fancy_hardware exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ] 新建子進程並運行一個帶指定參數的命令. 這個命令指定了seclabel(安全策略),user(全部者),group(用戶組). 直到這個命令運行完才能夠運行其餘命令,seclabel能夠設置爲 - 表示用默認值,argument表示屬性值. 直到子進程新建完畢,init進程才繼續執行.

exec_start <service>
啓動一個service,只有當執行結果返回,init進程才能繼續執行. 這個跟exec類似,只是將一堆參數的設置改在在service中定義

export <name> <value>
設置環境變量name-value. 這個環境變量將被全部已經啓動的service繼承

hostname <name>
設置主機名

ifup <interface>
開啓指定的網絡接口

insmod [-f] <path> [<options>]
安裝path下的模塊,指定參數options.
-f 表示強制安裝,即使是當前Linux內核版本與之不匹配

load_all_props
加載/system, /vendor等目錄下的屬性,這個用在init.rc中

load_persist_props
加載/data 下的持久化屬性. 這個用在init.rc中

loglevel <level>
設置日誌輸出等級,level表示等級

mkdir <path> [mode] [owner] [group]
建立一個目錄,path是路徑,mode是讀寫權限,默認值是755,owner是全部者,默認值root,group是用戶組,默認值是root. 若是該目錄已存在,則覆蓋他們的mode,owner等設置

mount_all <fstab> [ <path> ]\* [--<option>]
當手動觸發 "early" 和 "late"時,調用fs_mgr_mount_all 函數,指定fstab配置文件,並導入指定目錄下的.rc文件 詳情能夠查看init.rc文件中的有關定義

mount <type> <device> <dir> [ <flag>\* ] [<options>]
在dir目錄下掛載一個名叫device的設備
_flag 包括 "ro", "rw", "remount", "noatime", ...
options 包括 "barrier=1", "noauto_da_alloc", "discard", ... 用逗號分開,好比 barrier=1,noauto_da_alloc

restart <service>
終止後重啓一個service,若是這個service剛被重啓就什麼都不作,若是沒有在運行,就啓動

restorecon <path> [ <path>\* ]
恢復指定目錄下文件的安全上下文.第二個path是安全策略文件. 指定目錄不須要必須存在,由於它只須要在init中正確標記

restorecon_recursive <path> [ <path>\* ]
遞歸地恢復指定目錄下的安全上下文,第二個path是安全策略文件位置

rm <path>
調用 unlink(2)刪除指定文件. 最好用exec -- rm ...代替,由於這樣能夠確保系統分區已經掛載好

rmdir <path>
調用 rmdir(2) 刪除指定目錄

setprop <name> <value>
設置屬性name-value

setrlimit <resource> <cur> <max>
指定一個進程的資源限制

start <service>
啓動一個未運行的service

stop <service>
終止一個正在運行的service

swapon_all <fstab>
調用 fs_mgr_swapon_all,指定fstab配置文件.

symlink <target> <path>
在path下建立一個指向target的連接

sysclktz <mins_west_of_gmt>
重置系統基準時間(若是是格林尼治標準時間則設置爲0)

trigger <event>
觸發事件event,由一個action觸發到另外一個action隊列

umount <path>
卸載指定path的文件系統

verity_load_state
內部實現是加載dm-verity的狀態

verity_update_state <mount-point>
內部實現是設置dm-verity的狀態,而且設置partition.mount-point.verified的屬性. 用於adb從新掛載, 由於fs_mgr 不能直接設置它。

wait <path> [ <timeout> ]
查看指定路徑是否存在. 若是發現則返回,能夠設置超時時間,默認值是5秒

wait_for_prop <name> <value>
等待name屬性的值被設置爲value,若是name的值一旦被設置爲value,立刻繼續

write <path> <content>
打開path下的文件,並用write(2)寫入content內容. 若是文件不存在就會被建立,若是存在就會被覆蓋掉

Imports

import關鍵字不是一個命令,可是若是有.rc文件包含它就會立刻解析它裏面的section,用法以下:

import <path>
解析path下的.rc文件 ,括展當前文件的配置。若是path是個目錄,這個目錄下全部.rc文件都被解析,可是不會遞歸, import被用於如下兩個地方:
1.在初始化時解析init.rc文件
2.在mount_all時解析{system,vendor,odm}/etc/init/等目錄下的.rc文件

後面的內容主要是一些跟調試init進程相關的東西,好比init.svc.能夠查看service啓動的狀態, ro.boottime.init記錄一些關鍵的時間點,Bootcharting是一個圖表化的性能監測工具等,因爲與語法關係不大,就不做翻譯了

明白了.rc文件的語法,咱們再來看看init進程是如何解析.rc文件,將這些語法轉化爲實際執行的代碼的

2、 解析.rc文件

以前咱們在文檔中看到.rc文件主要有根目錄下的 /init.rc ,以及{system,vendor,odm}/etc/init/這三個目錄下的 *.rc , 而後就是若是有一個特殊目錄被設置的話,就替代這些目錄,明白這些,下面的代碼就好理解了.

int main(int argc, char** argv) {

    ...

    const BuiltinFunctionMap function_map;
    /* * 1.C++中::表示靜態方法調用,至關於java中static的方法 */
    Action::set_function_map(&function_map); //將function_map存放到Action中做爲成員屬性


    Parser& parser = Parser::GetInstance();//單例模式,獲得Parser對象
	/* * 1.C++中std::make_unique至關於new,它會返回一個std::unique_ptr,即智能指針,能夠自動管理內存 * 2.unique_ptr持有對對象的獨有權,兩個unique_ptr不能指向一個對象,不能進行復制操做只能進行移動操做 * 3.移動操做的函數是 p1=std::move(p) ,這樣指針p指向的對象就移動到p1上了 * 4.接下來的這三句代碼都是new一個Parser(解析器),而後將它們放到一個map裏存起來 * 5.ServiceParser、ActionParser、ImportParser分別對應service action import的解析 */
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    parser.AddSectionParser("import", std::make_unique<ImportParser>());
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {//若是ro.boot.init_rc沒有對應的值,則解析/init.rc以及/system/etc/init、/vendor/etc/init、/odm/etc/init這三個目錄下的.rc文件
        parser.ParseConfig("/init.rc");
        parser.set_is_system_etc_init_loaded(
                parser.ParseConfig("/system/etc/init"));
        parser.set_is_vendor_etc_init_loaded(
                parser.ParseConfig("/vendor/etc/init"));
        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    } else {//若是ro.boot.init_rc屬性有值就解析屬性值
        parser.ParseConfig(bootscript);
        parser.set_is_system_etc_init_loaded(true);
        parser.set_is_vendor_etc_init_loaded(true);
        parser.set_is_odm_etc_init_loaded(true);
    }
複製代碼

2.1 ParseConfig

定義在 platform/system/core/init/init_parser.cpp

首先是判斷傳入的是目錄仍是文件,其實他們都是調用ParseConfigFile,ParseConfigDir就是遍歷下該目錄中的文件,對文件排個序,而後調用ParseConfigFile.

bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        return ParseConfigDir(path);
    }
    return ParseConfigFile(path);
}
複製代碼

而ParseConfigFile就是讀取文件中的數據後,將數據傳遞給ParseData函數,最後遍歷section_parsers_調用其EndFile函數, EndFile後面再分析,由於是多態實現,咱們先看看ParseData

bool Parser::ParseConfigFile(const std::string& path) {
    LOG(INFO) << "Parsing file " << path << "...";
    Timer t;
    std::string data;
    if (!read_file(path, &data)) { //將數據讀取到data
        return false;
    }

    data.push_back('\n'); // TODO: fix parse_config.
    ParseData(path, data); //解析數據
    for (const auto& sp : section_parsers_) {
        sp.second->EndFile(path);
    }

    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
    return true;
}
複製代碼

2.2 ParseData

ParseData 定義在 platform/system/core/init/init_parser.cpp

ParseData經過調用next_token函數遍歷每個字符,以空格或""爲分割將一行拆分紅若干個單詞,調用T_TEXT將單詞放到args數組中, 當讀到回車符就調用T_NEWLINE,在section_parsers_這個map中找到對應的on service import的解析器,執行ParseSection,若是在 map中找不到對應的key,就執行ParseLineSection,當讀到0的時候,表示一個Section讀取結束,調用T_EOF執行EndSection.

void Parser::ParseData(const std::string& filename, const std::string& data) {
    //TODO: Use a parser with const input and remove this copy
    std::vector<char> data_copy(data.begin(), data.end()); //將data的內容複製到data_copy中
    data_copy.push_back('\0'); //追加一個結束符0

    parse_state state; //定義一個結構體
    state.filename = filename.c_str();
    state.line = 0;
    state.ptr = &data_copy[0];
    state.nexttoken = 0;

    SectionParser* section_parser = nullptr;
    std::vector<std::string> args;

    for (;;) {
        switch (next_token(&state)) { // 遍歷data_copy中每個字符
        case T_EOF: //若是是文件結尾,則調用EndSection
            if (section_parser) {
                section_parser->EndSection();
            }
            return;
        case T_NEWLINE://讀取了一行數據
            state.line++;
            if (args.empty()) {
                break;
            }
            /* * 1.section_parsers_是一個std:map * 2.C++中std:map的count函數是查找key,至關於Java中Map的contains * 3.section_parsers_中只有三個key,on service import,以前AddSectionParser函數加入 */
            if (section_parsers_.count(args[0])) { //判斷是否包含 on service import
                if (section_parser) {
                    section_parser->EndSection();
                }
                section_parser = section_parsers_[args[0]].get();//取出對應的parser
                std::string ret_err;
                if (!section_parser->ParseSection(args, &ret_err)) {//解析對應的Section
                    parse_error(&state, "%s\n", ret_err.c_str());
                    section_parser = nullptr;
                }
            } else if (section_parser) { //不包含 on service import則是command或option
                std::string ret_err;
                if (!section_parser->ParseLineSection(args, state.filename,
                                                      state.line, &ret_err)) {//解析command或option
                    parse_error(&state, "%s\n", ret_err.c_str());
                }
            }
            args.clear();
            break;
        case T_TEXT: //將讀取的一行數據放到args中,args以空格或""做爲分割,將一行數據拆分紅單詞放進數組中
            args.emplace_back(state.text);
            break;
        }
    }
}

複製代碼

這裏其實涉及到on service import對應的三個解析器ActionParser,ServiceParser,ImportParser,它們是在以前加入到section_parsers_這個map中的

Parser& parser = Parser::GetInstance();
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    parser.AddSectionParser("import", std::make_unique<ImportParser>());

    void Parser::AddSectionParser(const std::string& name,
                                  std::unique_ptr<SectionParser> parser) {
        section_parsers_[name] = std::move(parser);
    }
複製代碼

它們都是SectionParser的子類,SectionParser有四個純虛函數,分別是ParseSection、ParseLineSection、EndSection,EndFile.

class SectionParser {
public:
    virtual ~SectionParser() {
    }
    /* * 1.C++中純虛函數的定義格式是 virtual做爲修飾符,而後賦值給0,至關於Java中的抽象方法 * 2.若是不賦值給0,卻以virtual做爲修飾符,這種是虛函數,虛函數能夠有方法體,至關於Java中父類的方法,主要用於子類的重載 * 3.只要包含純虛函數的類就是抽象類,不能new,只能經過子類實現,這個跟Java同樣 */
    virtual bool ParseSection(const std::vector<std::string>& args, std::string* err) = 0;
    virtual bool ParseLineSection(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) const = 0;
    virtual void EndSection() = 0;
    virtual void EndFile(const std::string& filename) = 0;
};
複製代碼

接下來我將分析這三個Perser的ParseSection、ParseLineSection、EndSection,EndFile具體實現

2.3 ActionParser

定義在platform/system/core/init/action.cpp

咱們先看ParseSection,它先將args中下標1到結尾的數據複製到triggers數組中,而後是構建Action對象,調用InitTriggers,解析這些trigger

bool ActionParser::ParseSection(const std::vector<std::string>& args,
                                std::string* err) {
    std::vector<std::string> triggers(args.begin() + 1, args.end()); //將args複製到triggers中,除去下標0
    if (triggers.size() < 1) {
        *err = "actions must have a trigger";
        return false;
    }

    auto action = std::make_unique<Action>(false);
    if (!action->InitTriggers(triggers, err)) { //調用InitTriggers解析trigger
        return false;
    }

    action_ = std::move(action);
    return true;
}
複製代碼

InitTriggers經過比較是否以"property:"開頭,區分trigger的類型,若是是property trigger,就調用ParsePropertyTrigger, 若是是event trigger,就將args的參數賦值給event_trigger_,類型是string

bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
    const static std::string prop_str("property:");
    for (std::size_t i = 0; i < args.size(); ++i) {

        ...

        if (!args[i].compare(0, prop_str.length(), prop_str)) {
            if (!ParsePropertyTrigger(args[i], err)) {
                return false;
            }
        } else {
           ...
            event_trigger_ = args[i];
        }
    }

    return true;
}
複製代碼

ParsePropertyTrigger函數先是將字符以"="分割爲name-value,而後將name-value存入property_triggers_這個map中

bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
    const static std::string prop_str("property:");
    std::string prop_name(trigger.substr(prop_str.length())); //截取property:後的內容
    size_t equal_pos = prop_name.find('=');
    if (equal_pos == std::string::npos) {
        *err = "property trigger found without matching '='";
        return false;
    }

    std::string prop_value(prop_name.substr(equal_pos + 1)); //取出value
    prop_name.erase(equal_pos); //刪除下標爲equal_pos的字符,也就是刪除"="

    if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
    //將name-value存放到map中,emplace至關於put操做
        *err = "multiple property triggers found for same property";
        return false;
    }
    return true;
}
複製代碼

從上面看出,ParseSection函數的做用就是構造一個Action對象,將trigger條件記錄到Action這個對象中,若是是event trigger就賦值給event_trigger_, 若是是property trigger就存放到property_triggers_這個map中. 接下來咱們分析ParseLineSection

ParseLineSection是直接調用Action對象的AddCommand函數

bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
                                    const std::string& filename, int line,
                                    std::string* err) const {
    return action_ ? action_->AddCommand(args, filename, line, err) : false;
}
複製代碼

AddCommand看名字就大概知道是添加命令,它首先是作一些參數空值的檢查,而後是調用FindFunction查找命令對應的執行函數, 最後將這些信息包裝成Command對象存放到commands_數組中,這裏比較關鍵的就是FindFunction

bool Action::AddCommand(const std::vector<std::string>& args,
                        const std::string& filename, int line, std::string* err) {
    ... //一些參數檢查

    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);//查找命令對應的執行函數
    if (!function) {
        return false;
    }

    AddCommand(function, args, filename, line);
    return true;
}

void Action::AddCommand(BuiltinFunction f,
                        const std::vector<std::string>& args,
                        const std::string& filename, int line) {
    commands_.emplace_back(f, args, filename, line);//commands_是個數組,emplace_back就至關於add
}
複製代碼

FindFunction定義在platform/system/core/init/keyword_map.h

這個函數主要做用是經過命令查找對應的執行函數,好比.rc文件中定義chmod,那咱們得找到chmod具體去執行哪一個函數. 它首先是經過map()返回一個std:map,調用其find函數, find至關於Java中的get,可是返回的是entry,能夠經過entry ->first和entry ->second獲取key-value. 找到的value是一個結構體,裏面有三個值,第一個是參數最小數目,第二個是參數最大數目,第三個就是執行函數, 以後做了參數的數目檢查,也就是說命令後的參數要在最小值和最大值之間.

const Function FindFunction(const std::string& keyword, size_t num_args, std::string* err) const {
        using android::base::StringPrintf;

        auto function_info_it = map().find(keyword); //找到keyword對應的entry
        if (function_info_it == map().end()) { // end是最後一個元素後的元素,表示找不到
            *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
            return nullptr;
        }

        auto function_info = function_info_it->second;//獲取value

        auto min_args = std::get<0>(function_info);//獲取參數數量最小值
        auto max_args = std::get<1>(function_info);//獲取參數數量最大值
        if (min_args == max_args && num_args != min_args) {//將實際參數數量與最大值最小值比較
            *err = StringPrintf("%s requires %zu argument%s",
                                keyword.c_str(), min_args,
                                (min_args > 1 || min_args == 0) ? "s" : "");
            return nullptr;
        }

        if (num_args < min_args || num_args > max_args) {
            if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
                *err = StringPrintf("%s requires at least %zu argument%s",
                                    keyword.c_str(), min_args,
                                    min_args > 1 ? "s" : "");
            } else {
                *err = StringPrintf("%s requires between %zu and %zu arguments",
                                    keyword.c_str(), min_args, max_args);
            }
            return nullptr;
        }

        return std::get<Function>(function_info);//返回命令對應的執行函數
    }
複製代碼

咱們看看map()的實現,定義在platform/system/core/init/builtins.cpp

這個實現比較簡單,就是直接構造一個map,而後返回. 好比{"bootchart", {1,1,do_bootchart}}, 表示命令名稱叫bootchart,對應的執行函數是do_bootchart,容許傳入的最小和最大參數數量是1

BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); //表示size_t的最大值
    // clang-format off
    static const Map builtin_functions = {
        {"bootchart",               {1,     1,    do_bootchart}},
        {"chmod",                   {2,     2,    do_chmod}},
        {"chown",                   {2,     3,    do_chown}},
        {"class_reset",             {1,     1,    do_class_reset}},
        {"class_restart",           {1,     1,    do_class_restart}},
        {"class_start",             {1,     1,    do_class_start}},
        {"class_stop",              {1,     1,    do_class_stop}},
        {"copy",                    {2,     2,    do_copy}},
        {"domainname",              {1,     1,    do_domainname}},
        {"enable",                  {1,     1,    do_enable}},
        {"exec",                    {1,     kMax, do_exec}},
        {"exec_start",              {1,     1,    do_exec_start}},
        {"export",                  {2,     2,    do_export}},
        {"hostname",                {1,     1,    do_hostname}},
        {"ifup",                    {1,     1,    do_ifup}},
        {"init_user0",              {0,     0,    do_init_user0}},
        {"insmod",                  {1,     kMax, do_insmod}},
        {"installkey",              {1,     1,    do_installkey}},
        {"load_persist_props",      {0,     0,    do_load_persist_props}},
        {"load_system_props",       {0,     0,    do_load_system_props}},
        {"loglevel",                {1,     1,    do_loglevel}},
        {"mkdir",                   {1,     4,    do_mkdir}},
        {"mount_all",               {1,     kMax, do_mount_all}},
        {"mount",                   {3,     kMax, do_mount}},
        {"umount",                  {1,     1,    do_umount}},
        {"restart",                 {1,     1,    do_restart}},
        {"restorecon",              {1,     kMax, do_restorecon}},
        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
        {"rm",                      {1,     1,    do_rm}},
        {"rmdir",                   {1,     1,    do_rmdir}},
        {"setprop",                 {2,     2,    do_setprop}},
        {"setrlimit",               {3,     3,    do_setrlimit}},
        {"start",                   {1,     1,    do_start}},
        {"stop",                    {1,     1,    do_stop}},
        {"swapon_all",              {1,     1,    do_swapon_all}},
        {"symlink",                 {2,     2,    do_symlink}},
        {"sysclktz",                {1,     1,    do_sysclktz}},
        {"trigger",                 {1,     1,    do_trigger}},
        {"verity_load_state",       {0,     0,    do_verity_load_state}},
        {"verity_update_state",     {0,     0,    do_verity_update_state}},
        {"wait",                    {1,     2,    do_wait}},
        {"wait_for_prop",           {2,     2,    do_wait_for_prop}},
        {"write",                   {2,     2,    do_write}},
    };
    // clang-format on
    return builtin_functions;
}

複製代碼

接下來咱們看看EndSection,直接是調用ActionManager::GetInstance().AddAction

void ActionParser::EndSection() {
    if (action_ && action_->NumCommands() > 0) {
        ActionManager::GetInstance().AddAction(std::move(action_));
    }
}
複製代碼

AddAction首先是查找是否有存在的同名Action,若是有就將他們的命令合併,沒有就將它存入數組actions_中

void ActionManager::AddAction(std::unique_ptr<Action> action) {
    auto old_action_it =
        std::find_if(actions_.begin(), actions_.end(),
                     [&action] (std::unique_ptr<Action>& a) {
                         return action->TriggersEqual(*a);
                     });//find_if是集合中用於比較的模板,上面這種寫法是lambda表達式

    if (old_action_it != actions_.end()) {//在數組actions中找到Action說明已經存在同名,就合併command
        (*old_action_it)->CombineAction(*action);
    } else { //找不到就加入數組
        actions_.emplace_back(std::move(action));
    }
}

bool Action::TriggersEqual(const Action& other) const {
    return property_triggers_ == other.property_triggers_ &&
        event_trigger_ == other.event_trigger_;//比較以前記錄的event trigger和property trigger
}

void Action::CombineAction(const Action& action) {
    for (const auto& c : action.commands_) { //將新的Action中的command合併到老的Action
        commands_.emplace_back(c);
    }
}
複製代碼

EndFile是一個空實現,定義在platform/system/core/init/action.h

class ActionParser : public SectionParser {
public:
    ActionParser() : action_(nullptr) {
    }
    bool ParseSection(const std::vector<std::string>& args, std::string* err) override;
    bool ParseLineSection(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) const override;
    void EndSection() override;
    void EndFile(const std::string&) override { //空實現
    }
private:
    std::unique_ptr<Action> action_;
};
複製代碼

講了這麼多,小結一下ActionParser作的事情. 它有三個重要的重載函數,ParseSection、ParseLineSection、EndSection.

  • ParseSection函數的做用是構造一個Action對象,將trigger條件記錄到Action這個對象中
  • ParseLineSection做用是根據命令在一個map中找到對應的執行函數,而後將信息記錄到以前構造的Action中
  • EndSection做用是將前兩步構造的Action存入一個數組中,存入以前比較下數組中是否已經存在同名的Action,若是有就合併command

2.4 ServiceParser

定義在platform/system/core/init/service.cpp

咱們仍是分析它的四個函數ParseSection、ParseLineSection、EndSection、EndFile

ParseSection首先是判斷單詞個數至少有三個,由於必須有一個服務名稱和執行文件,而後是判斷名稱是否合法,主要是一些長度及內容的檢查,最後就是構造一個Service對象

bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                 std::string* err) {
    if (args.size() < 3) { // 傳入單詞個數至少三個
        *err = "services must have a name and a program";
        return false;
    }

    const std::string& name = args[1];
    if (!IsValidName(name)) {//檢查名稱是否合法
        *err = StringPrintf("invalid service name '%s'", name.c_str());
        return false;
    }

    std::vector<std::string> str_args(args.begin() + 2, args.end());
    service_ = std::make_unique<Service>(name, str_args);// 構造Service對象
    return true;
}
複製代碼

ParseLineSection直接執行Service的ParseLine函數

bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
                                     const std::string& filename, int line,
                                     std::string* err) const {
    return service_ ? service_->ParseLine(args, err) : false;
}
複製代碼

ParseLine的思路跟以前Action同樣,就是根據option名稱從map中找到對應的執行函數,而後執行這個函數. 這些執行函數主要做用就是對傳入參數作一些處理,而後將信息記錄到Service對象中

bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
    if (args.empty()) {
        *err = "option needed, but not provided";
        return false;
    }

    static const OptionParserMap parser_map;
    auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);//從map中找出執行函數

    if (!parser) {
        return false;
    }

    return (this->*parser)(args, err);//執行找到的這個函數
}
複製代碼

map()返回的map以下,定義在定義在platform/system/core/init/service.cpp中

Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    static const Map option_parsers = {
        {"capabilities",
                        {1,     kMax, &Service::ParseCapabilities}},
        {"class",       {1,     kMax, &Service::ParseClass}},
        {"console",     {0,     1,    &Service::ParseConsole}},
        {"critical",    {0,     0,    &Service::ParseCritical}},
        {"disabled",    {0,     0,    &Service::ParseDisabled}},
        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
        {"ioprio",      {2,     2,    &Service::ParseIoprio}},
        {"priority",    {1,     1,    &Service::ParsePriority}},
        {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
        {"oom_score_adjust",
                        {1,     1,    &Service::ParseOomScoreAdjust}},
        {"namespace",   {1,     2,    &Service::ParseNamespace}},
        {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
        {"setenv",      {2,     2,    &Service::ParseSetenv}},
        {"socket",      {3,     6,    &Service::ParseSocket}},
        {"file",        {2,     2,    &Service::ParseFile}},
        {"user",        {1,     1,    &Service::ParseUser}},
        {"writepid",    {1,     kMax, &Service::ParseWritepid}},
    };
    // clang-format on
    return option_parsers;
}
複製代碼

接下來咱們看看EndSection,直接調用ServiceManager的AddService函數

void ServiceParser::EndSection() {
    if (service_) {
        ServiceManager::GetInstance().AddService(std::move(service_));
    }
}
複製代碼

AddService的實現比較簡單,就是經過比較service的name,查看存放Service的數組services_中是否有同名的service,若是有就打印下錯誤日誌,直接返回, 若是不存在就加入數組中

void ServiceManager::AddService(std::unique_ptr<Service> service) {
    Service* old_service = FindServiceByName(service->name()); //查找services_中是否已存在同名service
    if (old_service) {
        LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
        return;
    }
    services_.emplace_back(std::move(service));//加入數組
}

Service* ServiceManager::FindServiceByName(const std::string& name) const {
    auto svc = std::find_if(services_.begin(), services_.end(),
                            [&name] (const std::unique_ptr<Service>& s) {
                                return name == s->name();
                            });//跟以前action同樣,遍歷數組進行比較,查找同名service
    if (svc != services_.end()) {
        return svc->get(); //找到就返回service
    }
    return nullptr;
}
複製代碼

EndFile依然是一個空實現,定義在platform/system/core/init/service.h

class ServiceParser : public SectionParser {
public:
    ServiceParser() : service_(nullptr) {
    }
    bool ParseSection(const std::vector<std::string>& args, std::string* err) override;
    bool ParseLineSection(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) const override;
    void EndSection() override;
    void EndFile(const std::string&) override { //空實現
    }
private:
    bool IsValidName(const std::string& name) const;

    std::unique_ptr<Service> service_;
};
複製代碼

從上面能夠看出,ServiceParser的處理跟ActionParser差很少,區別在於Action將執行函數存起來等待Trigger觸發時執行,Service找到執行函數後是立刻執行

2.4 ImportParser

定義在platform/system/core/init/import_parser.cpp

最後咱們看看ImportParser,ImportParser的ParseLineSection、EndSection都是空實現,只實現了ParseSection和EndFile, 由於它的語法比較單一,只有一行. 咱們來看看它的ParseSection函數

首先檢查單詞只能是兩個,由於只能是import xxx 這種語法,而後調用expand_props處理下參數,最後將結果放入數組imports_存起來

bool ImportParser::ParseSection(const std::vector<std::string>& args,
                                std::string* err) {
    if (args.size() != 2) { //檢查參數只能是兩個
        *err = "single argument needed for import\n";
        return false;
    }

    std::string conf_file;
    bool ret = expand_props(args[1], &conf_file); //處理第二個參數
    if (!ret) {
        *err = "error while expanding import";
        return false;
    }

    LOG(INFO) << "Added '" << conf_file << "' to import list";
    imports_.emplace_back(std::move(conf_file)); //存入數組
    return true;
}
複製代碼

expand_props 定義在platform/system/core/init/util.cpp ,主要做用就是找到{x.y}或x.y這種語法,將x.y取出來做爲name,去屬性系統中找對應的value,而後替換

bool expand_props(const std::string& src, std::string* dst) {
    const char* src_ptr = src.c_str();

    if (!dst) {
        return false;
    }

    /* - variables can either be $x.y or ${x.y}, in case they are only part * of the string. * - will accept ? as a literal $. * - no nested property expansion, i.e. ${foo.${bar}} is not supported, * bad things will happen * - ${x.y:-default} will return default value if property empty. */
    //這段英文大概的意思是 參數要麼是$x.y,要麼是${x.y},它們都是路徑的一部分,?表示字符 $ ,
    //${foo.${bar}}這種遞歸寫法是不支持的,由於會發生一些糟糕的事情
    //${x.y:-default}會將default做爲默認值返回,若是找不到對應的屬性值的話
    while (*src_ptr) {
        const char* c;

        c = strchr(src_ptr, '$');
        if (!c) { // 找不到$符號,直接將dst賦值爲src返回
            dst->append(src_ptr);
            return true;
        }

        dst->append(src_ptr, c);
        c++;

        if (*c == '$') { //跳過$
            dst->push_back(*(c++));
            src_ptr = c;
            continue;
        } else if (*c == '\0') {
            return true;
        }

        std::string prop_name;
        std::string def_val;
        if (*c == '{') { //找到 { 就準備找 }的下標,而後截取它們之間的字符串,對應${x.y}的狀況
            c++;
            const char* end = strchr(c, '}');
            if (!end) {
                // failed to find closing brace, abort.
                LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
                return false;
            }
            prop_name = std::string(c, end); //截取{}之間的字符串做爲name
            c = end + 1;
            size_t def = prop_name.find(":-"); //若是發現有 ":-" ,就將後面的值做爲默認值先存起來
            if (def < prop_name.size()) {
                def_val = prop_name.substr(def + 2);
                prop_name = prop_name.substr(0, def);
            }
        } else { //對應$x.y的狀況
            prop_name = c;
            LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
            c += prop_name.size();
        }

        if (prop_name.empty()) {
            LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
            return false;
        }

        std::string prop_val = android::base::GetProperty(prop_name, ""); //經過name在屬性系統中找對應的value,內部調用的是以前屬性系統的__system_property_find函數
        if (prop_val.empty()) { //沒有找到值就返回默認值
            if (def_val.empty()) {
                LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
                return false;
            }
            prop_val = def_val;
        }

        dst->append(prop_val);
        src_ptr = c;
    }

    return true;
}
複製代碼

EndFile的實現比較簡單,就是複製下ParseSection函數解析的.rc文件數組,而後遍歷數組,調用最開始的ParseConfig函數解析一個完整的路徑

void ImportParser::EndFile(const std::string& filename) {
    auto current_imports = std::move(imports_);
    imports_.clear();
    for (const auto& s : current_imports) {
        if (!Parser::GetInstance().ParseConfig(s)) {
            PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
        }
    }
}
複製代碼

由此,咱們將Android Init Language語法的轉化過程分析完畢,其實它們核心的解析器就三個,ActionParser,ServiceParser,ImportParser. 而這幾個解析器主要是實現ParseSection、ParseLineSection、EndSection、EndFile四個函數

  • ParseSection用於解析Section的第一行,好比
on early
service ueventd /sbin/ueventd
import /init.${ro.zygote}.rc
複製代碼
  • ParseLineSection用於解析Section的command或option,好比
write /proc/1/oom_score_adj -1000
class core 複製代碼
  • EndSection用於處理Action和Service同名的狀況,以及將解析的對象存入數組備用
  • EndFile只有在ImportParser中有用到,主要是解析導入的.rc文件

3、加入一些事件和一些Action

通過上一步的解析,系統從各類.rc文件中讀取了須要執行的Action和Service,可是仍是須要一些額外的配置,也須要加入觸發條件準備去觸發

// Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) parser.DumpState(); //打印一些當前Parser的信息,默認是不執行的

    ActionManager& am = ActionManager::GetInstance();

    am.QueueEventTrigger("early-init");//QueueEventTrigger用於觸發Action,這裏觸發 early-init事件

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    //QueueBuiltinAction用於添加Action,第一個參數是Action要執行的Command,第二個是Trigger

    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");

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

    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
複製代碼

3.1 QueueEventTrigger

定義在platform/system/core/init/action.cpp

它並無去觸發trigger,而是構造了一個EventTrigger對象,放到隊列中存起來

void ActionManager::QueueEventTrigger(const std::string& trigger) {
    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
}

class EventTrigger : public Trigger {
public:
    explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
    }
    bool CheckTriggers(const Action& action) const override {
        return action.CheckEventTrigger(trigger_);
    }
private:
    const std::string trigger_;
};
複製代碼

3.2 QueueBuiltinAction

定義在platform/system/core/init/action.cpp

這個函數有兩個參數,第一個參數是一個函數指針,第二參數是字符串. 首先是建立一個Action對象,將第二參數做爲Action觸發條件, 將第一個參數做爲Action觸發後的執行命令,而且又把第二個參數做爲命令的參數,最後是將Action加入觸發隊列並加入Action列表

void ActionManager::QueueBuiltinAction(BuiltinFunction func,
                                       const std::string& name) {
    auto action = std::make_unique<Action>(true);
    std::vector<std::string> name_vector{name};

    if (!action->InitSingleTrigger(name)) { //調用InitTriggers,以前講過用於將name加入Action的trigger列表
        return;
    }

    action->AddCommand(func, name_vector);//加入Action的command列表

    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));//將Action加入觸發隊列
    actions_.emplace_back(std::move(action));//加入Action列表
}
複製代碼

4、觸發全部事件並不斷監聽新的事件

以前的全部工做都是往各類數組、隊列裏面存入信息,並無真正去觸發,而接下來的工做就是真正去觸發這些事件,以及用epoll不斷監聽新的事件

while (true) {
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1; //epoll超時時間,至關於阻塞時間

         /* * 1.waiting_for_prop和IsWaitingForExec都是判斷一個Timer爲不爲空,至關於一個標誌位 * 2.waiting_for_prop負責屬性設置,IsWaitingForExe負責service運行 * 3.當有屬性設置或Service開始運行時,這兩個值就不爲空,直到執行完畢才置爲空 * 4.其實這兩個判斷條件主要做用就是保證屬性設置和service啓動的完整性,也能夠說是爲了同步 */
        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
            am.ExecuteOneCommand(); //執行一個command
        }
        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
            restart_processes(); //重啓服務

            // If there's a process that needs restarting, wake up in time for that.
            if (process_needs_restart_at != 0) { //當有進程須要重啓時,設置epoll_timeout_ms爲重啓等待時間
                epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
                if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
            }

            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout_ms = 0; //當還有命令要執行時,將epoll_timeout_ms設置爲0
        }

        epoll_event ev;
        /* * 1.epoll_wait與上一篇中講的epoll_create一、epoll_ctl是一塊兒使用的 * 2.epoll_create1用於建立epoll的文件描述符,epoll_ctl、epoll_wait都把它建立的fd做爲第一個參數傳入 * 3.epoll_ctl用於操做epoll,EPOLL_CTL_ADD:註冊新的fd到epfd中,EPOLL_CTL_MOD:修改已經註冊的fd的監聽事件,EPOLL_CTL_DEL:從epfd中刪除一個fd; * 4.epoll_wait用於等待事件的產生,epoll_ctl調用EPOLL_CTL_ADD時會傳入須要監聽什麼類型的事件, * 好比EPOLLIN表示監聽fd可讀,當該fd有可讀的數據時,調用epoll_wait通過epoll_timeout_ms時間就會把該事件的信息返回給&ev */
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
        if (nr == -1) {
            PLOG(ERROR) << "epoll_wait failed";
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();//當有event返回時,取出ev.data.ptr(以前epoll_ctl註冊時的回調函數),直接執行
            //上一篇中在signal_handler_init和start_property_service有註冊兩個fd的監聽,一個用於監聽SIGCHLD(子進程結束信號),一個用於監聽屬性設置
        }
    }

    return 0;
}
複製代碼

4.1 ExecuteOneCommand

定義在platform/system/core/init/action.cpp

從名字能夠看出,它只執行一個command,是的,只執行一個. 在函數一開始就從trigger_queue_隊列中取出一個trigger, 而後遍歷全部action,找出知足trigger條件的action加入待執行列表current_executing_actions_中, 接着從這個列表中取出一個action,執行它的第一個命令,並將命令所在下標自加1. 因爲ExecuteOneCommand外部是一個無限循環, 所以按照上面的邏輯一遍遍執行,將按照trigger表的順序,依次執行知足trigger條件的action,而後依次執行action中的命令.

void ActionManager::ExecuteOneCommand() {
    // Loop through the trigger queue until we have an action to execute
    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {//current_executing_actions_.empty保證了一次只遍歷一個trigger
        for (const auto& action : actions_) {//遍歷全部的Action
            if (trigger_queue_.front()->CheckTriggers(*action)) {//知足當前Trigger條件的就加入隊列current_executing_actions_
                current_executing_actions_.emplace(action.get());
            }
        }
        trigger_queue_.pop();//從trigger_queue_中踢除一個trigger
    }

    if (current_executing_actions_.empty()) {
        return;
    }

    auto action = current_executing_actions_.front();//從知足trigger條件的action隊列中取出一個action

    if (current_command_ == 0) {
        std::string trigger_name = action->BuildTriggersString();
        LOG(INFO) << "processing action (" << trigger_name << ")";
    }

    action->ExecuteOneCommand(current_command_);//執行該action中的第current_command_個命令

    // If this was the last command in the current action, then remove
    // the action from the executing list.
    // If this action was oneshot, then also remove it from actions_.
    ++current_command_; //下標加1
    if (current_command_ == action->NumCommands()) { //若是是最後一條命令
        current_executing_actions_.pop();//將該action從current_executing_actions_中踢除
        current_command_ = 0;
        if (action->oneshot()) {//若是action只執行一次,將該action從數組actions_中踢除
            auto eraser = [&action] (std::unique_ptr<Action>& a) {
                return a.get() == action;
            };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
        }
    }
}
複製代碼

4.1 restart_processes

定義在platform/system/core/init/init.cpp

restart_processes調用的實際上是ForEachServiceWithFlags函數,這個函數主要是遍歷services_數組,比較它們的flags是不是SVC_RESTARTING, 也就是當前service是不是等待重啓的,若是是就執行它的RestartIfNeeded函數

static void restart_processes() {
    process_needs_restart_at = 0;
    ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) {
        s->RestartIfNeeded(&process_needs_restart_at);
    });
}

void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
                                             void (*func)(Service* svc)) const {
    for (const auto& s : services_) { //遍歷全部service
        if (s->flags() & matchflags) {//找出flags是SVC_RESTARTING的,執行func,也就是傳入的RestartIfNeeded
            func(s.get());
        }
    }
}
複製代碼

4.2 RestartIfNeeded

定義在platform/system/core/init/service.cpp

這個函數將主要工做交給了Start,也就是具體的啓動service,可是交給它以前作了一些判斷,也就是5秒內只能啓動一個服務, 若是有多個服務,那麼後續的服務將進入等待

void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
    boot_clock::time_point now = boot_clock::now();
    boot_clock::time_point next_start = time_started_ + 5s; //time_started_是上一個service啓動的時間戳
    if (now > next_start) { //也就是說兩個服務進程啓動的間隔必須大於5s
        flags_ &= (~SVC_RESTARTING); // &= 加 ~ 至關於取消標記
        Start();
        return;
    }

    time_t next_start_time_t = time(nullptr) +
        time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
    if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
        *process_needs_restart_at = next_start_time_t;//若是兩個service啓動間隔小於5s,將剩餘時間賦值給process_needs_restart_at
    }
}
複製代碼

4.2 Start

定義在platform/system/core/init/service.cpp

Start是具體去啓動服務了,它主要是調用clone或fork建立子進程,而後調用execve執行配置的二進制文件,另外根據以前在.rc文件中的配置,去執行這些配置

bool Service::Start() {

    ... //清空標記,根據service的配置初始化console、SELinux策略等

    LOG(INFO) << "starting service '" << name_ << "'...";

    pid_t pid = -1;
    if (namespace_flags_) {//這個標記當service定義了namespace時會賦值爲CLONE_NEWPID|CLONE_NEWNS
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr); //以clone方式在新的namespace建立子進程
    } else {
        pid = fork();//以fork方式建立子進程
    }

    if (pid == 0) {//表示建立子進程成功

        ... //執行service配置的其餘參數,好比setenv、writepid等

        std::vector<char*> strs;
        ExpandArgs(args_, &strs);//將args_解析一下,好比有${x.y},而後賦值表strs
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) { //執行系統調用execve,也就是執行配置的二進制文件,把參數傳進去
            PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
        }

        _exit(127);
    }

    if (pid < 0) { //子進程建立失敗
        PLOG(ERROR) << "failed to fork for '" << name_ << "'";
        pid_ = 0;
        return false;
    }

    ... //執行service其餘參數如oom_score_adjust_,改變service運行狀態等
}
複製代碼

小結

這一階段Init進程作了許多重要的事情,好比解析.rc文件,這裏配置了全部須要執行的action和須要啓動的service, Init進程根據語法一步步去解析.rc,將這些配置轉換成一個個數組、隊列,而後開啓無限循環去處理這些數組、隊列中的command和service,而且經過epoll監聽子進程結束和屬性設置.

至此,我已經將Init進程的三個階段講解完了,下一篇我將講解.rc中配置的一個重要的service--zygote,它是咱們app程序的鼻祖.

相關文章
相關標籤/搜索