Android系統中的init進程與init語言
今天是農歷正月初一,一切又是全新的開始。在這個有著特殊意義的日子里,讓我們來一起學習一下Android系統的啟動是如何開始的。
init進程與init.rc
init進程是一切的開始,在Android系統中,所有進程的進程號都是不確定的,唯獨init進程的進程號一定是1。因為這個進程一定是系統起來的第一個進程。并且,init進程掌控了整個系統的啟動邏輯。
我們知道,Android可能運行在各種不同的平臺,不同的設備上。因此,啟動的邏輯是不盡相同的。 為了適應各種平臺和設備的需求,init進程的初始化工作通過 init.rc 配置文件來管理。init.rc以Android Init Language作為配置語法,下文我們簡稱Android Init Language為init語言。配置文件的主入口文件是/init.rc,這個文件會通過 import 關鍵字引入其他的配置文件。在這里,我們統稱這些文件為 init.rc 。
/init.rc可能 import 以下路徑中的.rc文件:
- /init.${ro.hardware}.rc 硬件廠商提供的主配置文件
- /system/etc/init/ 核心系統模塊的配置文件
- /vendor/etc/init/ SoC廠商提供的配置文件
- /odm/etc/init/ 設備廠商提供的配置文件
init語法說明
init語言以換行為語句分隔,以空格來為符號分隔。配置文件中支持五種類型的表達式:
- Action: 包含了一系列的Command
- Command: init語言中的命令
- Service: init進程啟動的服務
- Option: 對于服務的配置選項
- Import: 引入其他配置文件
這其中,Action和Service需要保證名稱唯一。
Action與Command
Action表達式的語法如下:
on <trigger> [&& <trigger>]*
<command>
<command>
<command>
這里的Trigger是Action執行的觸發器,當觸發器條件滿足時,command會被執行。觸發器有兩類:
- 事件觸發器 事件可以由”trigger”命令發出,也可以是init進程通過 QueueEventTrigger() 函數發出
- 屬性觸發器 當指定的屬性滿足時觸發
一個Action可以有多個屬性觸發器,但是只能包含一個事件觸發器。下面是一些實例:
- on boot && property:a=b 在”boot”事件發生時,并且屬性a的值是b時觸發
- on property:a=b && property:c=d 在屬性a的值是b并且屬性c的值是d時觸發
Action中支持的命令如下表所示:
Command | 參數格式 | 說明 |
---|---|---|
bootchart_init | - | 啟動bootchart |
chmod | octal-mode path | 改變文件的訪問權限 |
chown | owner group path | 改變文件的擁有者和組 |
class_start | serviceclass | 啟動指定類別的服務 |
class_stop | serviceclass | 停止并disable指定類別的服務 |
class_reset | serviceclass | 停止指定類別的服務,但是不disable它們 |
copy | src dst | 拷貝文件 |
domainname | name | 設置域名 |
enable | servicename | enable一個被disable的服務 |
exec | [ seclabel [ user [ group ]]] -- command [ argument ]* | fork一個子進程來執行指定的命令 |
export | name value | 導出環境變量 |
hostname | name | 設置host名稱 |
ifup | iterface | 使網卡在線 |
insmod | path | 安裝指定路徑的模塊 |
load_all_props | - | 從/system, /vendor等路徑載入屬性 |
load_persist_props | - | 載入持久化的屬性 |
loglevel | level | 設置內核的日志級別 |
mkdir | path [ mode ] [ owner ] [ group ] | 創建目錄 |
mount_all | fstab [ path ]* [-- option ] | 掛載文件系統并且導入指定的.rc文件 |
mount | type device dir [ flag ]* [ options ] | 掛載一個文件系統 |
powerctl | - | 內部實現使用 |
restart | service | 重啟服務 |
restorecon | path [ path ]* | 設定文件的安全上下文 |
restorecon_recursive | path [ path ]* | restorecon的遞歸版本 |
rm | path | 對于指定路徑調用unlink(2) |
rmdir | path | 刪除文件夾 |
setprop | name value | 設置屬性值 |
setrlimit | resource cur max | 指定資源的rlimit |
start | service | 啟動服務 |
stop | service | 停止服務 |
swapon_all | fstab | 在指定文件上調用fs_mgr_swapon_all |
symlink | target path | 創建符號鏈接 |
sysclktz | mins_west_of_gmt | 指定系統時鐘基準 |
trigger | event | 觸發一個事件 |
umount | path | unmount指定的文件系統 |
verity_load_state | - | 內部實現使用 |
verity_update_state | mount_point | 內部實現使用 |
wait | path [ timeout ] | 等待某個文件存在直到超時 |
write | path content | 寫入內容到指定文件 |
Service與Option
Service是init進程啟動的可執行程序。服務可以選擇在自己退出之后,由init將其重啟。
Service表達式的語法格式如下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
Option是對服務的修飾,它們影響著init進程如何以及何時啟動服務。所有支持的option如下表所示:
option | 參數格式 | 說明 |
---|---|---|
critical | - | 標識為系統關鍵服務,該服務若退出多次將導致系統重啟到recovery模式 |
disabled | - | 不會隨著類別自動啟動,必須明確start |
setenv | name value | 為啟動的進程設置環境變量 |
socket | name type perm [ user [ group [ seclabel ]]] | 創建Unix Domain Socket |
user | username | 在執行服務之前切換用戶 |
group | groupname [ groupname ]* | 在執行執行之前切換組 |
seclabel | seclabel | 在執行服務之前切換seclabel |
oneshot | - | 一次性服務,死亡之后不用重啟 |
class | name | 指定服務的類別 |
onrestart | - | 當服務啟動時執行命令 |
writepid | file… | 寫入子進程的pid到指定文件 |
這其中有兩個特別重要的進程就是: zygote 和 system_server 進程。
- zygote 的中文意思是“受精卵“。這是一個很有寓意的名稱:所有的應用進程都是由 zygote fork出來的子進程,因此zygote進程是所有應用進程的父進程。
- system_server 這個進程正如其名稱一樣,這是一個系統服務器。Framework層的幾乎所有服務都位于這個進程中。這其中就包括管理四大組件的 ActivityManagerService 。
Import
import是一個關鍵字,并不是一個命令。可以在.rc文件中通過這個關鍵字來加載其他的.rc文件。它的語法很簡單:
import path
path可以是另外一個.rc文件,也可以是一個文件夾。如果是文件夾,那么這個文件夾下面的所有文件都會被導入,但是不會循環加載子目錄。
init.rc代碼實例
AOSP中包含了Android系統需要的最基本的.rc文件,它們位于這個路徑:/system/core/rootdir/ 。
我們選取其中了一兩個代碼片段來了解一下:
# /system/core/rootdir/init.rc
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
# Disable sysrq from keyboard
write /proc/sys/kernel/sysrq 0
# Set the security context of /adb_keys if present.
restorecon /adb_keys
# Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
mkdir /mnt 0775 root system
# Set the security context of /postinstall if present.
restorecon /postinstall
start ueventd
on init
sysclktz 0
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /default.prop /dev/urandom
# Backward compatibility.
symlink /system/etc /etc
symlink /sys/kernel/debug /d
# Link /vendor to /system/vendor for devices without a vendor partition.
symlink /system/vendor /vendor
...
這是根目錄/init.rc文件中一開始的代碼片段。有了前面的講解之后,這段代碼應當還是比較好理解的。在這段代碼中:
- 一開始通過import關鍵字引入了其他幾個.rc文件
- 設定了一個事件為early-init的Action
- 設定了一個事件init的Action
“eraly-init”和”init”事件都是由init進程發出的。
下面,我們再來看另外一個代碼片段:
# /system/core/rootdir/init.zygote32.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
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
writepid /dev/cpuset/foreground/tasks
這段代碼定義了Andorid系統中一個非常重要的服務: zygote 。這個服務是通過可執行命令/system/bin/app_process啟動的,啟動的時候傳遞了參數: -Xzygote /system/bin --zygote --start-system-server 。
關于Zygote我們已經在另外一篇文章中講解過了,參見這里Zygote進程。
結束語
來自:http://qiangbo.space/2017-01-27/AndroidAnatomy_Init/