這是一個連載的博文系列,我將持續爲你們提供儘量透徹的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
本文涉及到的文件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
複製代碼
定義在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服務
有一個特殊的目錄可能被用來替換上面的三個默認目錄,這主要是爲了支持工廠模式和其餘非標準的啓動模式,上面三個目錄用於正常的啓動過程
這三個用於擴展的目錄是
- /system/etc/init/ 用於系統自己,好比SurfaceFlinger, MediaService, and logcatd.
- /vendor/etc/init/ 用於SoC(系統級核心廠商,如高通),爲他們提供一些核心功能和服務
- /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
這個定義有三種觸發方式:
- 在初始化時,屬性a=b,屬性c=d.
- 在屬性c=d的狀況下,屬性a被改成b.
- 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_hardwareexec [ <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文件,將這些語法轉化爲實際執行的代碼的
以前咱們在文檔中看到.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); } 複製代碼
定義在 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; } 複製代碼
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具體實現
定義在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.
定義在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找到執行函數後是立刻執行
定義在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取出來做爲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四個函數
on early service ueventd /sbin/ueventd import /init.${ro.zygote}.rc 複製代碼
write /proc/1/oom_score_adj -1000 class core 複製代碼
通過上一步的解析,系統從各類.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"); 複製代碼
定義在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_;
};
複製代碼
定義在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列表 } 複製代碼
以前的全部工做都是往各類數組、隊列裏面存入信息,並無真正去觸發,而接下來的工做就是真正去觸發這些事件,以及用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; } 複製代碼
定義在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)); } } } 複製代碼
定義在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()); } } } 複製代碼
定義在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 } } 複製代碼
定義在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程序的鼻祖.