1. 程式人生 > >Android 8.0 系統啟動流程之init.rc語法規則(六)

Android 8.0 系統啟動流程之init.rc語法規則(六)

1、概述

    init經過前兩個階段後,已經建立了屬性系統和SELinux系統,但是init程序還需要執行很多其他的操作,還要啟動許多關鍵的系統服務,但是如果都是像屬性系統和SELinux系統那樣一行行程式碼去做,顯得有點雜亂繁瑣,而且不容易擴充套件,所以Android系統引入了init.rc。
    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的一些語法。

2、Android Init Language語法

定義在platform/system/core/init/README.md

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

2.1 action

action的格式如下:

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

以on開頭,trigger是判斷條件,command是具體執行一些操作,當滿足trigger條件時,執行這些command。

trigger可以是一個字串,如:

on early //表示當trigger earlyQueueEventTrigger("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=* // *表示任意值

條件可以是多個,用&&連線,如:

//表示當zygote-start觸發並且ro.crypto.state屬性值為unencrypted時觸發
on zygote-start && property:ro.crypto.state=unencrypted

command就是一些具體的操作,如:

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

2.2 service

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部門.

2.3 Options

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

console

console [<console>]

Service需要控制檯. 第二個引數console的意思是可以設定你想要的控制檯型別,預設控制檯是/dev/console ,
/dev 這個字首通常是被忽略的,比如你要設定控制檯 /dev/tty0 ,那麼只需要設定為console tty0

critical


critical

表示Service是嚴格模式. 如果這個Service在4分鐘內退出超過4次,那麼裝置將重啟進入recovery模式

disabled

disabled

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

setenv

setenv <name> <value>

在Service啟動時設定name-value的環境變數

socket

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

file <path> <type>

開啟一個檔案,並將fd返回給這個Service. type 只能是 "r", "w" or "rw". 它對應的本地實現在libcutils的
android_get_control_file 

user

user <username>

在啟動Service前將user改為username,預設啟動時user為root(或許預設是無).在Android M版本,如果一個程序想擁有
Linux capabilities(相當於Android中的許可權吧),也只能通過設定這個值. 以前,一個程式要想有Linux 
capabilities,必須先以root身份執行,然後再降級到所需的uid.現在已經有一套新的機制取而代之,
它通過fs_config允許廠商賦予特殊二進位制檔案Linux capabilities. 這套機制的說明文件在
http://source.android.com/devices/tech/config/filesystem.html.當使用這套新的機制時,
程式可以通過user引數選擇自己所需的uid,而不需要以root許可權執行. 在Android O版本,程式可以通過
capabilities引數直接申請所需的能力,參見下面的capabilities說明

group


group <groupname> [ <groupname>\* ]

在啟動Service前將group改為第一個groupname,第一個groupname是必須有的,
預設值為root(或許預設值是無),第二個groupname可以不設定,用於追加組(通過setgroups).

capabilities

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 <seclabel>

在啟動Service前將seclabel設定為seclabel. 主要用於在rootfs上啟動的service,比如ueventd, adbd.
在系統分割槽上執行的service有自己的SELinux安全策略,如果不設定,預設使用init的安全策略.

oneshot

oneshot

退出後不再重啟

class

class <name> [ <name>\* ]Service指定class名字. 同一個class名字的Service會被一起啟動或退出,預設值是"default",第二個name可以不設定,
用於service組.

animation

animation class

animation class 主要包含為開機動畫或關機動畫服務的service. 它們很早被啟動,而且直到關機最後一步才退出.
它們不允許訪問/data 目錄,它們可以檢查/data目錄,但是不能開啟/data目錄,而且需要在/data不能用時也正常工作。

onrestart

onrestart 

在Service重啟時執行命令.

writepid

writepid <file> [ <file>\* ]

當Service呼叫fork時將子程序的pid寫入到指定檔案. 用於cgroup/cpuset的使用,當/dev/cpuset/下面沒有檔案
但ro.cpuset.default的值卻不為空時,將pid的值寫入到/dev/cpuset/cpuset_name/tasks檔案中

priority

priority <priority>

設定程序優先順序. 在-20~19之間,預設值是0,能過setpriority實現

namespace

namespace <pid|mnt>

當fork這個service時,設定pid或mnt標記

oom_score_adjust

oom_score_adjust <value>

設定子程序的 /proc/self/oom_score_adj 的值為 value,在 -10001000之間.

Triggers

Triggers

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

Triggers分為事件Trigger和屬性Trigger

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

屬性Trigger是在屬性被設定或發生改變時觸發. 格式是'property:<name>=<value>''property:<name>=*',它會在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.

2.4 Commands

bootchart

bootchart [start|stop]

啟動或終止bootcharting. 這個出現在init.rc檔案中,但是隻有在/data/bootchart/enabled檔案存在的時候才有效,
否則不能工作

chmod

chmod <octal-mode> <path>

修改檔案讀寫許可權

chown

chown <owner> <group> <path>

修改檔案所有者或所屬使用者組

class_start

class_start <serviceclass>

啟動所有以serviceclass命名的未啟動的service(service有一個name,也有個class,這裡的serviceclass就是
class,class_start和後面的start是兩種啟動方式,class_start是class形式啟動,start是name形式啟動)

class_stop

class_stop <serviceclass> 

終止所有以serviceclass命名的正在執行的service

class_reset

class_reset <serviceclass>

終止所有以serviceclass命名的正在執行的service,但是不禁用它們. 它們可以稍後被class_start重啟

copy

copy <src> <dst>

複製一個檔案,與write相似,比較適合二進位制或比較大的檔案.

對於src,從連結檔案、world-writable或group-writable複製是不允許的.

對於dst,如果目標檔案不存在,則預設許可權是0600,如果存在就覆蓋掉

domainname

domainname <name>

設定域名

enable

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

exec_start <service>

啟動一個service,只有當執行結果返回,init程序才能繼續執行. 這個跟exec相似,只是將一堆引數的設定改在在service中定義

export

export <name> <value>

設定環境變數name-value. 這個環境變數將被所有已經啟動的service繼承

hostname

hostname <name> 

設定主機名

ifup

ifup <interface>

開啟指定的網路介面

insmod

insmod [-f] <path> [<options>]

安裝path下的模組,指定引數options.

-f 表示強制安裝,即便是當前Linux核心版本與之不匹配

load_all_props

load_all_props

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

load_persist_props

load_persist_props

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

loglevel

loglevel <level>

設定日誌輸出等級,level表示等級

mkdir

mkdir <path> [mode] [owner] [group]

建立一個目錄,path是路徑,mode是讀寫許可權,預設值是755,owner是所有者,預設值root,group是使用者組,
預設值是root.如果該目錄已存在,則覆蓋他們的mode,owner等設定

mount_all

mount_all <fstab> [ <path> ]\* [--<option>]

當手動觸發 "early""late"時,呼叫fs_mgr_mount_all 函式,指定fstab配置檔案,並匯入指定目錄下的.rc檔案
詳情可以檢視init.rc檔案中的有關定義

mount

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

restart <service>

終止後重啟一個service,如果這個service剛被重啟就什麼都不做,如果沒有在執行,就啟動

restorecon

restorecon <path> [ <path>\* ]

恢復指定目錄下檔案的安全上下文.第二個path是安全策略檔案. 指定目錄不需要必須存在,因為它只需要在init中正確標記

restorecon_recursive

restorecon_recursive <path> [ <path>\* ]

遞迴地恢復指定目錄下的安全上下文,第二個path是安全策略檔案位置

rm

rm <path>

呼叫 unlink(2)刪除指定檔案. 最好用exec -- rm ...代替,因為這樣可以確保系統分割槽已經掛載好

rmdir

rmdir <path>

呼叫 rmdir(2) 刪除指定目錄

setprop

setprop <name> <value>

設定屬性name-value 

setrlimit

setrlimit <resource> <cur> <max>

指定一個程序的資源限制

start

start <service> 

啟動一個未執行的service

stop

stop <service>

終止一個正在執行的service

swapon_all

swapon_all <fstab>

呼叫 fs_mgr_swapon_all,指定fstab配置檔案.

symlink

symlink <target> <path>

在path下建立一個指向target的連結

sysclktz

sysclktz <mins_west_of_gmt>

重置系統基準時間(如果是格林尼治標準時間則設定為0)

trigger

trigger <event>

觸發事件event,由一個action觸發到另一個action佇列

umount

umount <path>

解除安裝指定path的檔案系統

verity_load_state

verity_load_state

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

verity_update_state

verity_update_state <mount-point>

內部實現是設定dm-verity的狀態,並且設定partition.mount-point.verified的屬性. 用於adb重新掛載,
因為fs_mgr 不能直接設定它。 

wait


wait <path> [ <timeout> ]

檢視指定路徑是否存在. 如果發現則返回,可以設定超時時間,預設值是5秒

wait_for_prop

wait_for_prop <name> <value>

等待name屬性的值被設定為value,如果name的值一旦被設定為value,馬上繼續

write

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檔案,將這些語法轉化為實際執行的程式碼的

3、小結

本文解釋了Android啟動檔案init.rc的語法規則,下一篇將分析一下init程序是如何解析這個啟動配置檔案。未完待續。。。