Android 深入理解AIL語言與init.rc文件

init.rc簡介

init.rc文件由系統第一個啓動的init程序進行解析.它由"Android Init Language"語言編寫而成.init.rc文件可以在你android設備根目錄下找到.還記得我們上次編譯的Android源碼麼?如果你已經編譯過源碼了,那麼可以在out/target/generic/root/目錄下找到該文件.

要想讀懂init.rc文件,首先要掌握Android Init Language語言,即AIL.在/system/core/init/下有一份readme.txt文件,爲我們詳細介紹了有關AIL的知識.我們下面的學習同樣是藉助了該文檔來的.


AIL語法

AIL語言非常簡單,主要包括兩部分:結構語法及註釋語法.下面我們就這兩點進行說明

結構語法

AIL語言包含主要包含五種結構語法:

  1. Actions
  2. Services
  3. Options
  4. Commands
  5. Imports

需要注意,AIL採用是面向行的代碼風格,即用換行符作爲一條語句的分隔符,也就是在init.rc中以一條語句通常佔據一行.如果一行寫不下,可以在行尾添加反斜槓來鏈接到下一行,換言之,通過行尾添加反斜槓符可以將多行代碼鏈接爲一行代碼.

init.rc有許多Service和Action組成.那麼什麼是Service和Action呢?
Action和Service顯式聲明瞭一個語句塊,而Commands和Options則分別用來定義Actions和Service(你可以理解爲這是Action或者Service的屬性).

另外,我們聲明的Commands和Options屬於最近聲明的語句塊,即就近原則.需要注意,在第一個語句塊之前的commands和options會被忽略.

每個Actions或者Services應該有唯一的名字.對於名字重複的情況,Action和Service有自己不同的處理方式:

如果第二個定義的Action的名字和之前存在Action的名字相同,第二個Action中定義的Commands將會被添加到已經存在的同名Action中.如果第二個定義的Service的名字和之前存在的Service的名字相同,第二個Service會被忽略並輸出錯誤信息.

註釋語法

AIL中的註釋語法和Shell腳本一致,以#開頭即可


結構語法詳解

Actions

Actions代表一些Action.Action代表一組命令,它包含一個觸發器,該觸發器決定了何時執行這個Action,即在什麼情況下才能執行該Action中的定義命令.當一些條件滿足觸發器的條件時,該Action中定義的命令會被添加到要執行命令隊列的尾部(如果這組命令已經在隊列中,則不會再次添加).

當一個Action從隊列移除時,該Action定義的命令會依次被執行.

Action的格式如下:

 

on <trgger> [&& <trigger>]*
   <command>
   <command>
   <command>
   ...

不難發現Action都是以on開始,隨後會定義觸發器(trigger),接着便是爲其定義命令(Commmand).在開始講解Trigger和Command之前,我們先來看一段Action的示例代碼:

 

on boot
    # 初始化網絡
    ifup lo
    hostname localhost
    domainname localdomain

trigger

trigger即我們上面所說的觸發器,本質上是一個字符串,能夠匹配某種包含該字符串的事件.
trigger又被細分爲事件觸發器(event trigger)和屬性觸發器(property trigger).

事件觸發器可由"trigger"命令或初始化過程中通過QueueEventTrigger()觸發,通常是一些事先定義的簡單字符串,例如:boot,late-init
屬性觸發器是當指定屬性的變量值變成指定值時觸發,其格式爲property:<name>=*

一個Action可以有多個屬性觸發器,但是最多有一個事件觸發器.下面我們看兩個例子:

 

on boot && property:a=b

該Action只有在boot事件發生時,並且屬性a和b相等的情況下才會被觸發.

 

on property:a=b && property:c=d

該Action會在以下三種情況被觸發:

  • 在啓動時,如果屬性a的值等於b並且屬性c的值等於d
  • 在屬性c的值已經是d的情況下,屬性a的值被更新爲b
  • 在屬性a的值已經是b的情況下,屬性c的值被更新爲d

當前AIL中常用的有以下幾種事件觸發器:

類型 說明
boot init.rc被裝載後觸發
device-added-<path> 指定設備被添加時觸發
device-removed-<path> 指定設備被移除時觸發
service-exited-<name> 在特定服務(service)退出時觸發
early-init 初始化之前觸發
late-init 初始化之後觸發
init 初始化時觸發

Commands

Commands代表一組命令,在爲Action設置了觸發器後,就需要爲其定義一組命令(command)了.AIL中內置了衆多的命令,下面我們做個簡單的說明:

命令 解釋
bootchart_init 如果配置了bootcharing,則啓動.包含在默認的init.rc中
chmod 更改文件權限
chown <owner> <group> <path> 更改文件的所有者和組
calss_start <serviceclass> 啓動指定類別服務下的所有未啓動的服務
class_stop <serviceclass> 停止指定類別服務類下的所有已運行的服務
class_reset <serviceclass> 停止指定類別的所有服務(服務還在運行),但不會禁用這些服務.後面可以通過class_start重啓這些服務
copy <src> <dst> 複製文件,對二進制/大文件非常有用
domainname <name> 設置域名稱
enable <servicename> 啓用已經禁用的服務
exec [ <seclabel> [ <user> [ <group> ]* ]]
--<command> [ <argument> ]*
fork一個進程執行指定命令,如果有參數,則帶參數執行
export <name> 在全局環境中,將<name>變量的值設置爲<value>,即以鍵值對的方式設置全局環境變量.這些變量對之後的任何進程都有效
hostname 設置主機名
ifup <interface> 啓動某個網絡接口
insmod [-f] <path> [<options>] 加載指定路徑下的驅動模塊。-f強制加載,即不管當前模塊是否和linux kernel匹配
load_all_props 從/system,/vendor加載屬性。默認包含在init.rc
load_persist_props 當/data被加密時,加載固定屬性
loglevel <level> 設置kernel日誌等級
mkdir <path> [mode] [owner] [group] 在制定路徑下創建目錄
mount_all <fstab> [ <path> ]* 在給定的fs_mgr-format上調用fs_mgr_mount和引入rc文件
mount <type> <device> <dir>[ <flag> ]* [<options>] 掛載指定設備到指定目錄下.
powerct 用來應對sys.powerctl中系統屬性的變化,用於系統重啓
restart <service> 重啓制定服務,但不會禁用該服務
restorecon <path> [ <path> ]* 恢復指定文件到file_contexts配置中指定的安全上線文環境
restorecon_recursive <path> [ <path> ]* 以遞歸的方式恢復指定目錄到file_contexts配置中指定的安全上下文中
rm <path> 刪除指定路徑下的文件
rmdir <path> 刪除制定路徑下的目錄
setprop <name> <value> 將系統屬性<name>的值設置爲<value>,即以鍵值對的方式設置系統屬性
setrlimit <resource> <cur> <max> 設置資源限制
start <service> 啓動服務(如果該服務還未啓動)
stop <service> 關閉服務(如果該服務還未停止)
swapon_all <fstab>  
symlink <target> <path> 創建一個指向<path>的符合鏈接<target>
sysclktz <mins_west_of_gmt> 設置系統時鐘的基準,比如0代表GMT,即以格林尼治時間爲準
trigger <event> 觸發一個事件,將該action排在某個action之後(用於Action排隊)
verity_load_state  
verity_update_state <mount_point>  
wait <path> [ <timeout> ] 等待一個文件是否存在,存在時立刻返回或者超時後返回.默認超時事件是5s
write <path> <content> 寫內容到指定文件中

Services

Services代表一些Service.Service是一些在系統初始化時就啓動或者退出時需要重啓的程序.其格式如下:

 

service <name> <pathname> [ <argument> ]*
        <option>
        <option>
        ...

不難發現,首先需要爲服務定義名字,並指定程序路徑,然後便是通過option來修飾服務.同樣先來看一下示例:

 

service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

Options

Options代表一些option.option用來修飾服務,決定了服務在什麼時候運行以及怎樣運行.AIL中提供了非常多的option,下面我們做個簡單說明:

選項 解釋
console 服務需要一個控制檯.
critical 表示這是一個關鍵設備服務.如果4分鐘內此服務退出4次以上,那麼這個設備將重啓進入recovery模式
disabled 服務不會自動啓動,必須通過服務名顯式啓動
setenv <name> <value> 在進程啓動過程中,將環境變量<name>的值設置爲<value>,即以鍵值對的方式設置環境變量
socket <name> <type> <perm> [ <user> [ <group> [seclabel]]] 創建一個unix域下的socket,其被命名/dev/socket/<name>. 並將其文件描述符fd返回給服務進程.其中,type必須爲dgram,stream或者seqpacke,user和group默認是0.seclabel是該socket的SELLinux的安全上下文環境,默認是當前service的上下文環境,通過seclabel指定.
user <username> 在執行此服務之前切換用戶名,當前默認的是root.自Android M開始,即使它要求linux capabilities,也應該使用該選項.很明顯,爲了獲得該功能,進程需要以root用戶運行
group <groupname> 在執行此服務之前切換組名,除了第一個必須的組名外,附加的組名用於設置進程的補充組(藉助setgroup()函數),當前默認的是root
seclabel <seclabel> 在執行該服務之前修改其安全上下文,默認是init程序的上下文
oneshot 當服務退出時,不重啓該服務
class <name> 爲當前service設定一個類別.相同類別的服務將會同時啓動或者停止,默認類名是default.
onrestart 當服務重啓時執行該命令
priority <priority> 設置服務進程的優先級.優先級取值範圍爲-20~19,默認是0.可以通過setpriority()設置

Imports

用來引入一個要解析的其他配置文件,通常用於當前配置文件的擴展.
其格式如下:

 

import <path>

如果path是個一個目錄,則該目錄下的每個.rc文件都被引入.

在初始化過程中,共有兩次使用import來引入.rc文件:

  1. 在初始化引導期間,引入/init.rc文件
  2. 在執行mount_all命令時,引入/{system,vendor,odm}/etc/init/或者指定路徑下的.rc文件

我們來看看init.rc文件引入的.rc文件:

 

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc

Properties

Properties代表Init進程運行中的一些屬性信息.在Init運行中,通過以下屬性能夠獲取當前程序內部信息:

類型 說明
init.svc.<name> 指定名稱服務的狀態,有stopped,stopping,runing,restarting這種四種狀態
init.action 獲取當前正在執行的action
init.command 獲取當前正在執行的command

文件示例

到現在爲止,有關AIL相關的知識基本介紹完畢,下面截取init.rc文件中的一段來做個簡單的說明:

 

//引入其他要解析的rc文件
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc

#定義了一個action,在init初始化之前觸發
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
    
    #啓動ueventd服務
    start ueventd
    
    #...省略多行...
    
#定義ueventd服務,設置服務爲/sbin/ueventd
service ueventd /sbin/ueventd
    class core#爲其設置類名爲core
    critical#表明這是一個關鍵服務
    seclabel u:r:ueventd:s0 #設置其安全上下文

總結

AIL是一種非常簡單的語言,主要用於定義啓動流程中需要做的事情.


原文鏈接:https://www.jianshu.com/p/d08e1affd5ec

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章