1. 程式人生 > >Android系統init程序啟動及init.rc全解析

Android系統init程序啟動及init.rc全解析

這是一篇用心寫的部落格,也希望大家用心看並幫忙找到文章的改進之處,謝謝;

服務啟動機制

  1. system/core/init/init.c檔案main函式中parse_config_file(init.rc)讀取並解析init.rc檔案內容。將service資訊放置到system/core/init/init_parser.cpp的service_list中
  2. system/core/init/init.c檔案main函式繼續執行restart_servie_if_needed(…) -> service_start(…) -> Execve(…)建立service程序;

為了讓大夥看得更明白,上個圖先《總體啟動框架圖》:
這裡寫圖片描述

init.rc 簡介

目前Linux有很多通訊機制可以在使用者空間和核心空間之間互動,例如裝置驅動檔案(位於/dev目錄中)、記憶體檔案(/proc、/sys目錄等)。瞭解Linux的同學都應該知道Linux的重要特徵之一就是一切都是以檔案的形式存在的,例如,一個裝置通常與一個或多個裝置檔案對應。這些與核心空間互動的檔案都在使用者空間,所以在Linux核心裝載完,需要首先建立這些檔案所在的目錄。而完成這些工作的程式就是本文要介紹的init。Init是一個命令列程式。其主要工作之一就是建立這些與核心空間互動的檔案所在的目錄。當Linux核心載入完後,要做的第一件事就是呼叫init程式,也就是說,init是使用者空間執行的第一個程式。

儘管init完成的工作不算很多,不過程式碼還是非常複雜的。Init程式並不是由一個原始碼檔案組成的,而是由一組原始碼檔案的目標檔案連結而成的。這些檔案位於如下的目錄。

需要明白的是,這些init.rc只是語法檔案,並不是程式,真正的入口則是上面提到的system/core/init/init.c
因為init.c檔案比較大,在文章的第二部分我會簡要的通過main函式分析init啟動流程;

init.rc有兩個,分別位於:
./system/core/rootdir/init.rc
./bootable/recovery/etc/init.rc
從目錄上大致可以猜測,這兩個init.rc使用場景不一樣,一個是刷機用到的,也就是進入recorvery模式,一個是正常啟動用到的;我們這裡重點分析的是上面那個,也是init.c關聯的那個;

init.rc語法結構解析

要了解init.rc是怎麼解析的,我們需要先看看說明文件,說明文件在,當然也可以看下熱心網友的中文對照版本
init.rc位於/bootable/recovery/etc/init.rc

Android初始化語言包含了四種類型的宣告:
Actions(行為)、Commands(命令)、Services(服務)和Options(選項)

所有這些都是以行為單位的,各種記號由空格來隔開。
C語言風格的反斜槓號可用於在記號間插入空格。
雙引號也可用於防止字串被空格分割成多個記號。
行末的反斜槓用於折行,註釋行以井號(#)開頭(允許以空格開頭)。

需要注意的是,這個只是一個語法檔案,就像一個xml檔案一樣,沒有執行順序的,解析器通過讀這個檔案獲取想要的資料,包括service,action等

Actions和Services宣告一個新的分組Section。所有的命令或選項都屬於最近宣告的分組。位於第一個分組之前的命令或選項將會被忽略。
Actions和Services有唯一的名字。如果有重名的情況,第二個申明的將會被作為錯誤忽略。

Actions

Actions(行為)是一系列命令的開始
Actions代表一些Action.Action代表一組命令(Commands),Actions都有一個trigger(觸發器),該觸發器決定了何時執行這個Action,即在什麼情況下才能執行該Action中的定義命令.當一些條件滿足觸發器的條件時,該Action中定義的命令會被新增到要執行命令佇列的尾部(如果這組命令已經在佇列中,則不會再次新增).

佇列中的每一個action都被依次提取出,而這個action中的每個command(命令)在一個Action從佇列移除時,該Action定義的命令會依次被執行.

Action的格式如下:

on <trgger> [&& <trigger>]*
   <command1>
   <command2>
   <command3>
   ...

on後面跟著一個觸發器,當trigger被觸發時,command1,command2,command3,會依次執行,直到下一個Action或下一個Service。

簡單來說,Actions就是Android在啟動時定義的一個啟動指令碼,當條件滿足時,會執行該指令碼,腳本里都是一些命令commands,不同的指令碼用on來區分。

Triggers(觸發器)

trigger即我們上面所說的觸發器,本質上是一個字串,能夠匹配某種包含該字串的事件.
trigger又被細分為事件觸發器(event trigger)和屬性觸發器(property trigger).
Triggers(觸發器)是一個用於匹配特定事件型別的字串,用於使Actions發生。

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

一個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                    初始化時觸發(在 /init.conf (啟動配置檔案)被裝載之後)

Init的觸發是由init.c裡的函式action_for_each_trigger來決定的(在main函式中被呼叫)。

Services

Services(服務)是一個程式,以 service開頭,由init程序啟動,一般運行於另外一個init的子程序,所以啟動service前需要判斷對應的可執行檔案是否存在。init生成的子程序,定義在rc檔案,其中每一個service,在啟動時會通過fork方式生成子程序。Services(服務)的形式如下:

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

其中:

  • name:服務名
  • pathname:當前服務對應的程式位置
  • option:當前服務設定的選項
  • argument 可選引數

init.rc檔案詳解

為了方便理解,我把整個init.rc解析一邊,便於大家瞭解整個流程;如果想要了解recovery下的init語法解析,參考這篇文章《recovery下的init.rc語法解析》
程式碼量比較大,如果覺得看起來費勁,可以挑綠色部分看;

# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
#

"【import <filename>一個init配置檔案,擴充套件當前配置。】"
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc

"【觸發條件early-init,在early-init階段呼叫以下行】"
on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000
    "【開啟路徑為<path>的一個檔案,並寫入一個或多個字串】"
    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
    write /sys/fs/selinux/checkreqprot 0

    # Set the security context for the init process.
    # This should occur before anything else (e.g. ueventd) is started.
    "【這段指令碼的意思是init程序啟動之後就馬上呼叫函式setcon將自己的安全上下文設定為“u:r:init:s0”,即將init程序的domain指定為init。】"
    setcon u:r:init:s0

    # Set the security context of /adb_keys if present.
    "【恢復指定檔案到file_contexts配置中指定的安全上線文環境】"
    restorecon /adb_keys

    "【執行start ueventd的命令。ueventd是一個service後面有定義】 "
    start ueventd

    "【mkdir <path> [mode] [owner] [group]   //建立一個目錄<path>,可以選擇性地指定mode、owner以及group。如果沒有指定,預設的許可權為755,並屬於root使用者和root組。】"
    # create mountpoints
    mkdir /mnt 0775 root system

on init
    "【設定系統時鐘的基準,比如0代表GMT,即以格林尼治時間為準】"
    sysclktz 0

"【設定kernel日誌等級】"
loglevel 6 ####
    write /proc/bootprof "INIT: on init start" ####

    "【symlink <target> <path>    //建立一個指向<path>的軟連線<target>。】"
    # Backward compatibility
    symlink /system/etc /etc
    symlink /sys/kernel/debug /d

    # Right now vendor lives on the same filesystem as system,
    # but someday that may change.
    symlink /system/vendor /vendor

    "【建立一個目錄<path>,可以選擇性地指定mode、owner以及group。】"
    # Create cgroup mount point for cpu accounting
    mkdir /acct
    mount cgroup none /acct cpuacct
    mkdir /acct/uid

    "【mount <type> <device> <dir> [ <mountoption> ]   //在目錄<dir>掛載指定的裝置。<device> 可以是以 mtd@name 的形式指定一個mtd塊裝置。<mountoption>包括 ro、rw、remount、noatime、 ...】"
    # Create cgroup mount point for memory
    mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000
    mkdir /sys/fs/cgroup/memory 0750 root system
    mount cgroup none /sys/fs/cgroup/memory memory
    write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1
    "【chown <owner> <group> <path>   //改變檔案的所有者和組。】"

    "【後面的一些行因為類似,就省略了】"
    .....

# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
    "【停止指定類別服務類下的所有已執行的服務】"
    class_stop charger
    "【觸發一個事件,將該action排在某個action之後(用於Action排隊)】"
    trigger late-init

# Load properties from /system/ + /factory after fs mount.
on load_all_props_action
    "【從/system,/vendor載入屬性。預設包含在init.rc】"
    load_all_props

# Indicate to fw loaders that the relevant mounts are up.
on firmware_mounts_complete
    "【刪除指定路徑下的檔案】"
    rm /dev/.booting

# Mount filesystems and start core system services.
on late-init
    "【觸發一個事件。用於將一個action與另一個 action排列。】"
    trigger early-fs
    trigger fs
    trigger post-fs
    trigger post-fs-data

    # Load properties from /system/ + /factory after fs mount. Place
    # this in another action so that the load will be scheduled after the prior
    # issued fs triggers have completed.
    trigger load_all_props_action

    # Remove a file to wake up anything waiting for firmware.
    trigger firmware_mounts_complete

    trigger early-boot
    trigger boot


on post-fs
    ...
    "【一些創造目錄,建立連結,更改許可權的操作,這裡省略】"

on post-fs-data
    ...
    "【一些創造目錄,建立連結,更改許可權的操作,這裡省略】"

    "【恢復指定檔案到file_contexts配置中指定的安全上線文環境】"
    restorecon /data/mediaserver

    "【將系統屬性<name>的值設定為<value>,即以鍵值對的方式設定系統屬性】"
    # Reload policy from /data/security if present.
    setprop selinux.reload_policy 1

    "【以遞迴的方式恢復指定目錄到file_contexts配置中指定的安全上下文中】"
    # Set SELinux security contexts on upgrade or policy update.
    restorecon_recursive /data

    # If there is no fs-post-data action in the init.<device>.rc file, you
    # must uncomment this line, otherwise encrypted filesystems
    # won't work.
    # Set indication (checked by vold) that we have finished this action
    #setprop vold.post_fs_data_done 1

on boot
    "【初始化網路】"
    # basic network init
    ifup lo
    "【設定主機名為localhost】"
    hostname localhost
    "【設定域名localdomain】"
    domainname localdomain

    "【設定資源限制】"
    # set RLIMIT_NICE to allow priorities from 19 to -20
    setrlimit 13 40 40

    "【這裡省略了一些chmod,chown,等操作,不多解釋】"
   ...


    # Define default initial receive window size in segments.
    setprop net.tcp.default_init_rwnd 60

    "【重啟core服務】"
    class_start core

on nonencrypted
    class_start main
    class_start late_start

on property:vold.decrypt=trigger_default_encryption
    start defaultcrypto

on property:vold.decrypt=trigger_encryption
    start surfaceflinger
    start encrypt

on property:sys.init_log_level=*
    loglevel ${sys.init_log_level}

on charger
    class_start charger

on property:vold.decrypt=trigger_reset_main
    class_reset main

on property:vold.decrypt=trigger_load_persist_props
    load_persist_props

on property:vold.decrypt=trigger_post_fs_data
    trigger post-fs-data

on property:vold.decrypt=trigger_restart_min_framework
    class_start main

on property:vold.decrypt=trigger_restart_framework
    class_start main
    class_start late_start

on property:vold.decrypt=trigger_shutdown_framework
    class_reset late_start
    class_reset main

on property:sys.powerctl=*
    powerctl ${sys.powerctl}

# system server cannot write to /proc/sys files,
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.
on property:sys.sysctl.extra_free_kbytes=*
    write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}

# "tcp_default_init_rwnd" Is too long!
on property:sys.sysctl.tcp_def_init_rwnd=*
    write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}

"【守護程序】"
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

"【日誌服務程序】"
service logd /system/bin/logd
    class core
    socket logd stream 0666 logd logd
    socket logdr seqpacket 0666 logd logd
    socket logdw dgram 0222 logd logd
    seclabel u:r:logd:s0

"【Healthd是android4.4之後提出來的一種中介模型,該模型向下監聽來自底層的電池事件,向上傳遞電池資料資訊給Framework層的BatteryService用以計算電池電量相關狀態資訊】"
service healthd /sbin/healthd
    class core
    critical
    seclabel u:r:healthd:s0

"【控制檯程序】"
service console /system/bin/sh
    "【為當前service設定一個類別.相同類別的服務將會同時啟動或者停止,預設類名是default】"
    class core
    "【服務需要一個控制檯】"
    console
    "【服務不會自動啟動,必須通過服務名顯式啟動】"
    disabled
    "【在執行此服務之前切換使用者名稱,當前預設的是root.自Android M開始,即使它要求linux capabilities,也應該使用該選項.很明顯,為了獲得該功能,程序需要以root使用者執行】"
    user shell
    seclabel u:r:shell:s0

on property:ro.debuggable=1
    start console

# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /sbin/adbd --root_seclabel=u:r:su:s0
    class core
    "【建立一個unix域下的socket,其被命名/dev/socket/<name>. 並將其檔案描述符fd返回給服務程序.其中,type必須為dgram,stream或者seqpacke,user和group預設是0.seclabel是該socket的SELLinux的安全上下文環境,預設是當前service的上下文環境,通過seclabel指定】"
    socket adbd stream 660 system system
    disabled
    seclabel u:r:adbd:s0

# adbd on at boot in emulator
on property:ro.kernel.qemu=1
    start adbd

"【記憶體管理服務,記憶體不夠釋放記憶體】"
service lmkd /system/bin/lmkd
    class core
    critical
    socket lmkd seqpacket 0660 system system

"【ServiceManager是一個守護程序,它維護著系統服務和客戶端的binder通訊。
在Android系統中用到最多的通訊機制就是Binder,Binder主要由Client、Server、ServiceManager和Binder驅動程式組成。其中Client、Service和ServiceManager執行在使用者空間,而Binder驅動程式執行在核心空間。核心元件就是Binder驅動程式了,而ServiceManager提供輔助管理的功能,無論是Client還是Service進行通訊前首先要和ServiceManager取得聯絡。而ServiceManager是一個守護程序,負責管理Server並向Client提供查詢Server的功能。】"
service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    "【servicemanager 服務啟動時會重啟zygote服務】"
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

"【Vold是Volume Daemon的縮寫,它是Android平臺中外部儲存系統的管控中心,是管理和控制Android平臺外部儲存裝置的後臺程序】"
service vold /system/bin/vold
    class core
    socket vold stream 0660 root mount
    ioprio be 2

"【Netd是Android系統中專門負責網路管理和控制的後臺daemon程式】"
service netd /system/bin/netd
    class main
    socket netd stream 0660 root system
    socket dnsproxyd stream 0660 root inet
    socket mdns stream 0660 root system
    socket fwmarkd stream 0660 root inet

"【debuggerd是一個daemon程序,在系統啟動時隨著init程序啟動。主要負責將程序執行時的資訊dump到檔案或者控制檯中】"
service debuggerd /system/bin/debuggerd
    class main

service debuggerd64 /system/bin/debuggerd64
    class main

"【Android RIL (Radio Interface Layer)提供了Telephony服務和Radio硬體之間的抽象層】"
# for using TK init.modem.rc rild-daemon setting
#service ril-daemon /system/bin/rild
#    class main
#    socket rild stream 660 root radio
#    socket rild-debug stream 660 radio system
#    user root
#    group radio cache inet misc audio log

"【提供系統 範圍內的surface composer功能,它能夠將各種應用 程式的2D、3D surface進行組合。】"
service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    onrestart restart zygote

"【DRM可以直接訪問DRM clients的硬體。DRM驅動用來處理DMA,記憶體管理,資源鎖以及安全硬體訪問。為了同時支援多個3D應用,3D圖形卡硬體必須作為一個共享資源,因此需要鎖來提供互斥訪問。DMA傳輸和AGP介面用來發送圖形操作的buffers到顯示卡硬體,因此要防止客戶端越權訪問顯示卡硬體。】"
#make sure drm server has rights to read and write sdcard ####
service drm /system/bin/drmserver
    class main
    user drm
    # group drm system inet drmrpc ####
    group drm system inet drmrpc sdcard_r ####

"【媒體服務,無需多說】"
service media /system/bin/mediaserver
    class main
    user root ####
#   google default ####
#   user media    ####
    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm media sdcard_r system net_bt_stack ####
#   google default ####
#   group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm ####

    ioprio rt 4

"【裝置加密相關服務】"
# One shot invocation to deal with encrypted volume.
service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
    disabled
    "【當服務退出時,不重啟該服務】"
    oneshot
    # vold will set vold.decrypt to trigger_restart_framework (default
    # encryption) or trigger_restart_min_framework (other encryption)

# One shot invocation to encrypt unencrypted volumes
service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
    disabled
    oneshot
    # vold will set vold.decrypt to trigger_restart_framework (default
    # encryption)

"【開機動畫服務】"
service bootanim /system/bin/bootanimation
    class core
    user graphics
#    group graphics audio ####
    group graphics media audio ####
    disabled
    oneshot

"【在Android系統中,PackageManagerService用於管理系統中的所有安裝包資訊及應用程式的安裝解除安裝,但是應用程式的安裝與解除安裝並非PackageManagerService來完成,而是通過PackageManagerService來訪問installd服務來執行程式包的安裝與解除安裝的。】"
service installd /system/bin/installd
    class main
    socket installd stream 600 system system

service flash_recovery /system/bin/install-recovery.sh
    class main
    seclabel u:r:install_recovery:s0
    oneshot

"【vpn相關的服務】"
service racoon /system/bin/racoon
    class main
    socket racoon stream 600 system system
    # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
    group vpn net_admin inet
    disabled
    oneshot

"【android中有mtpd命令可以連線vpn】"
service mtpd /system/bin/mtpd
    class main
    socket mtpd stream 600 system system
    user vpn
    group vpn net_admin inet net_raw
    disabled
    oneshot

service keystore /system/bin/keystore /data/misc/keystore
    class main
    user keystore
    group keystore drmrpc

"【可以用dumpstate 獲取裝置的各種資訊】"
service dumpstate /system/bin/dumpstate -s
    class main
    socket dumpstate stream 0660 shell log
    disabled
    oneshot

"【mdnsd 是多播 DNS 和 DNS 服務發現的守護程式。】"
service mdnsd /system/bin/mdnsd
    class main
    user mdnsr
    group inet net_raw
    socket mdnsd stream 0660 mdnsr inet
    disabled
    oneshot

"【觸發關機流程繼續往下走】"
service pre-recovery /system/bin/uncrypt
    class main
    disabled
    "【當服務退出時,不重啟該服務】"
    oneshot

init.c全解析

接下來我們具體分析以下這個main函式的執行過程;可能比較長,大家耐心看一下:


int main( int argc, char **argv )
{
    #創 建一些linux根檔案系統中的目錄
    mkdir( "/dev", 0755 );
    mkdir( "/proc", 0755 );
    mkdir( "/sys", 0755 );

    mount( "tmpfs", "/dev", "tmpfs", 0, "mode=0755" );
    mkdir( "/dev/pts", 0755 );
    mkdir( "/dev/socket", 0755 );
    mount( "devpts", "/dev/pts", "devpts", 0, NULL );
    mount( "proc", "/proc", "proc", 0, NULL );
    mount( "sysfs", "/sys", "sysfs", 0, NULL );
    #init的 標準輸入,標準輸出,標準錯誤檔案描述符定向到__null__,意味著沒有輸入和輸出,它的輸入和輸出全部寫入到Log中
    open_devnull_stdio();
    #初始化 log 寫入init進 資訊
    log_init();
    #讀取並 且解析init.rc檔案(這個檔案在根目錄下)
    parse_config_file( "/init.rc" );
    #取得硬體 為列印我們的裝置名fs100
    get_hardware_name();
    snprintf( tmp, sizeof(tmp), "/init.%s.rc", hardware );
    #讀取並 且解析硬體相關的init指令碼檔案,
    parse_config_file( tmp );
    #觸發在init指令碼檔案中名字為early-init的action,並且執行其commands,其實是: on early-init
    action_for_each_trigger( "early-init", action_add_queue_tail );
    drain_action_queue();
    #初始化動態裝置管理,裝置檔案有變化時反應給核心,後面具體解釋
    device_fd = device_init(); # 初 始 化 設 備 管 理 務
    #載入啟動動畫,如果動畫開啟失敗,則在螢幕上列印: A N D R O I D字樣。
    if ( load_565rle_image( INIT_IMAGE_FILE ) )
    {
        fd = open( "/dev/tty0", O_WRONLY );
        if ( fd >= 0 )
        {
            const char *msg;
            msg = "\n"
                  "\n"
                  "\n"
                  879         "\n"
                  "\n"
                  "\n"
                  "\n" /* console is 40 cols x 30 lines */
                  "\n"
                  "\n"
                  "\n"
                  "\n"
                  "\n"
                  "\n"
                  "\n"
                  /* "             A N D R O I D ";開機動畫 */
                  write( fd, msg, strlen( msg ) );
            close( fd );
        }
    }

    #觸發 在init指令碼檔案中名字為init的action,並且執行其commands,其實是:on init
    action_for_each_trigger( "init", action_add_queue_tail );
    drain_action_queue();
    #啟動系統屬性服務: system property service
    property_set_fd = start_property_service();
    #建立socket用來處理孤兒程序訊號
    if ( socketpair( AF_UNIX, SOCK_STREAM, 0, s ) == 0 )
    {
        signal_fd   = s[0];
        signal_recv_fd  = s[1];
        fcntl( s[0], F_SETFD, FD_CLOEXEC );
        fcntl( s[0], F_SETFL, O_NONBLOCK );
        fcntl( s[1], F_SETFD, FD_CLOEXEC );
        fcntl( s[1], F_SETFL, O_NONBLOCK );
    }
    #觸發 在init指令碼檔案中名字為early-boot和boot的action,並且執行其commands,其實是:on early-boot和on boot
    action_for_each_trigger( "early-boot", action_add_queue_tail );
    action_for_each_trigger( "boot", action_add_queue_tail );
    drain_action_queue();
    #啟動所有屬性變化觸發命令,其實是: on property:ro.xx.xx=xx
    queue_all_property_triggers();
    drain_action_queue();
    #進入 死迴圈()
    for (;; )
    {
    #啟 動所有init指令碼中宣告的service,
    #如 :266 service servicemanager /system/bin/servicemanager
    #user system
    #critical
    #onrestart restart zygote
    #onrestart restart media
    restart_processes();
    #多路監聽裝置管理,子程序執行狀態,屬性服務
        nr = poll( ufds, fd_count, timeout );
        if ( nr <= 0 )
            continue;
        if ( ufds[2].revents == POLLIN )
        {
            read( signal_recv_fd, tmp, sizeof(tmp) );
            while ( !wait_for_one_process( 0 ) )
                ;
            continue;
        }

        if ( ufds[0].revents == POLLIN )
            handle_device_fd( device_fd );

        if ( ufds[1].revents == POLLIN )
            handle_property_set_fd( property_set_fd );
        if ( ufds[3].revents == POLLIN )
            handle_keychord( keychord_fd );
    }

    return(0);
}