android init進程分析 init腳本解析和處理
RC文件格式
rc文件是linux中常見的啟動載入階段運行的文件。rc是run commands的縮寫。基本上能夠理解為在啟動階段運行的一些列命令。android init進程啟動時,也會運行此啟動腳本文件,init.rc。init.rc的寫法稍有點復雜,具體可參考 /system/core/init下的readme文件。腳本基本組成是由四類,為:
- commands: 命令
- action: 動作
- services: 服務
- options: 選項
該語言的語法包含下列約定:
- 全部類型的語句都是基於行(line-oriented)的,一個語句包括若幹個tokens,token之間通過空格字符分隔。
假設一個token中須要包括空格字符,則須要通過C語言風格的反斜線(‘\‘)來轉義。或者使用雙引號把整個token引起來。
反斜線還能夠出如今一行的末尾。表示下一行的內容仍然屬於當前語句。
- 以‘#‘開始的行是凝視行(凝視僅僅能是行的第一個非空字符,這個千萬要註意)。
- 動作(Actions)和服務(Services)語句隱含表示一個新的段落(section)的開始。
全部的指令(commands)和選項(options)歸屬於上方近期的一個段落。在第一個段落之前的指令(commands)和選項(options)是無效的。
- 動作(Actions)和服務(Services)擁有唯一性的名字。假設出現重名。那麽後出現的定義將被作為錯誤忽略掉【眼下還是忽略,不是覆蓋!
】。
事實上。嚴格來說,應該就僅僅有兩種類別Action和Service:
- Action是由特定trigger下的一堆command組成
- Service是由一堆options描寫敘述的daemon服務組成
動作(Action)
動作(Action)是一個有名字的指令(commands)序列。每一個動作(Action)都定義一個觸發條件(trigger)。用於指示什麽時候運行這個動作。
在實際處理中。當與動作的觸發器匹配的事件發生時。該動作下的命令,將被加入到一個即將被運行的隊列的隊尾(除非它已經在隊列中)。
隊列中的每個命令被依次取出運行。
一個動作定義的形式例如以下:
on <trigger> <command> <command> <command>
服務(Services)
服務是初始化程序須要啟動的一些程序,初始化程序還有可能會在這些程序退出之後重新啟動它們。Services take一個服務定義的形式例如以下:
service <name> <pathname> [ <argument> ]* <option> <option> ...
選項(Options)
選項將影響控制初始化程序執行服務的時機和方法。可能的選項例如以下表(註意,options僅僅是作為服務的附屬部分的)..
說明:
disabled
服務在啟動所屬的class的時候,不會自己主動隨之啟動,僅僅有明白指明要啟動此服務時才會啟動(通常每一個服務都會屬於一個class中,由classkeyword描寫敘述)
socket <name> <type> <perm> [ <user> [ <group> ] ]
Create a unix domain socket named/dev/socket/<name> and pass its fd to the launched process. Valid<type> values includedgram andstream.user andgroup default to 0.
user <username>
在exec啟動此服務之前,改變進程的user。默認user是root
group <groupname> [ <groupname> ]*
周期exec啟動服務之前。改變此服務進程的group. 默認group是root。將服務增加特定的group,能夠獲取一些特定的權限。
capability [ <capability> ]+
Set linux capability before exec‘ing this service
oneshot
服務僅僅運行一次。對於非daemon程序。必需要使用onshot模式。
class <name>
設定服務的class名字,全部的服務都必須設置一個,假設不設置。默認是屬於default class的。同一個class中的服務。能夠通過命令class_start統一啟動。
觸發器(Triggers)
觸發器是一個字符串,用於匹配特定的事件。這些事件將觸發觸發器所屬動作(Action)的運行。
指令(Commands)
支持哪些指令。各個android版本號不盡同樣。詳情可參考/system/core/init下的readme或最好看代碼
屬性(Properties)
初始化程序(Init)能夠依據須要改動一些系統的屬性。readme中說的三個:
init.action
Equal to the name of the action currently being executed or "" if none.
init.command
Equal to the command being executed or "" if none.
init.svc.<name>
State of a named service ("stopped", "running", or "restarting").
實際init進程僅僅看到更新 init.svc.<name>, 每一個服務都會創建一個這種property。能夠通過 getprop | grep init.svc 看到每一個服務的狀態,狀態切換的時候,會同一時候更新。
void notify_service_state(const char *name, const char *state) { char pname[PROP_NAME_MAX]; int len = strlen(name); if ((len + 10) > PROP_NAME_MAX) return; snprintf(pname, sizeof(pname), "init.svc.%s", name); property_set(pname, state); }其它兩個沒意義,實際也沒用,可能當初設計有吧,但最後也沒用(comman/action都是開機時運行,且持續時間都非常短,沒太大用處)。
init 的parser
Init的parser是比較簡單的自己主動機,代碼也不算復雜,主要parser的是三部分:
- service: 每一個service的描寫敘述及屬性,parser好了,放在service_list中,以便以後通過actions的命令啟動。
- actions: parser每一個動作, on XXX及後繼的command。就是一個action。這個parser好後。也是放在action_list中,以便在init中啟動。
- import: init.rc 能夠支持像c中的include一樣,包括其它init.rc。這個支持比較簡單,並非相當於在import位置插入被import的代碼。而是在主init.rc都parser好後。再依次parser被包括的rc文件。
init.rc腳本命令的運行
腳本解析好後,會將各個Actions依照時間順序,分別推到action_queue中,再一個一個運行。
actions依照trigger的時間順序,依次運行的是。默認trigger的順序例如以下:
action_for_each_trigger("early-init", action_add_queue_tail); action_for_each_trigger("init", action_add_queue_tail); 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); action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail);
因此。各個命令須要特別註意處理好依賴。比方在init階段。是不可能訪問data分區的。另外還要註意一下。 同一個階段中,import的rc文件,是在後面一些運行的(依據import文件的parser順序)。
service的啟動時間。須要註意一下,也是通過action中的command運行的。 比方在on boot階段:
on boot ... class_start core
這樣。在boot階段運行到這個命令時。全部屬於core這一類的服務,都會被逐個啟動。
android init進程分析 init腳本解析和處理