Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> 詳細分析Android的init進程的啟動過程

詳細分析Android的init進程的啟動過程

編輯:Android開發實例

一、Android Init.c執行流程

Android中的內核啟動後,kernel會啟動第一個用戶級別的進程:init,它是一個由內核啟動的用戶級進程。內核自行啟動(已經被載入內存,開始運行,並已初始化所有的設備驅動程序和數據結構等)之後,就通過啟動一個用戶級程序init的方式,完成引導進程。init始終是第一個進程。
PS:可以通過:ps aux | grep init命令來查看其Pid為1。
init進程對應的代碼在android源碼目錄中的:system/core/init/init.c中。
789 int main(int argc, char **argv)
790 {
# 創建一些linux根文件系統中的目錄
817     mkdir("/dev", 0755);
818     mkdir("/proc", 0755);
819     mkdir("/sys", 0755);
820
821     mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
822     mkdir("/dev/pts", 0755);
823     mkdir("/dev/socket", 0755);
824     mount("devpts", "/dev/pts", "devpts", 0, NULL);
825     mount("proc", "/proc", "proc", 0, NULL);
826     mount("sysfs", "/sys", "sysfs", 0, NULL); 
# 打開標准輸入,標准輸出,標准錯誤文件描述符
834     open_devnull_stdio();
# 讀取並且解析init.rc文件
838     parse_config_file("/init.rc");
# 取得硬件名
844     get_hardware_name();
845     snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
# 讀取並且解析硬件相關的init腳本文件
846     parse_config_file(tmp);
# 觸發在init腳本文件中名字為early-init的action,並且執行其commands,其實是: on early-init
848     action_for_each_trigger("early-init", action_add_queue_tail);
849     drain_action_queue();
# 初始化動態設備管理,設備文件有變化時反應給內核,後面具體解釋
852     device_fd = device_init();
# 加載啟動動畫,如果動畫打開失敗,則在屏幕上打印: A N D R O I D字樣。
872     if( load_565rle_image(INIT_IMAGE_FILE) ) {
 873     fd = open("/dev/tty0", O_WRONLY);
 874     if (fd >= 0) {
 875         const char *msg;
 876             msg = "\n"
 877         "\n"
 878         "\n"
 879         "\n"
 880         "\n"
 881         "\n"
 882         "\n"  // console is 40 cols x 30 lines
 883         "\n"
 884         "\n"
 885         "\n"
 886         "\n"
 887         "\n"
 888         "\n"
 889         "\n"
 890         //"             A N D R O I D ";
 891         write(fd, msg, strlen(msg));
 892         close(fd);
 893     }
 894     }
 895
# 觸發在init腳本文件中名字為init的action,並且執行其commands,其實是:on init
919     action_for_each_trigger("init", action_add_queue_tail);
 920     drain_action_queue();
# 啟動系統屬性服務: system property service
927     property_set_fd = start_property_service();

# 創建socket用來處理孤兒進程信號
930     if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
 931         signal_fd = s[0];
 932         signal_recv_fd = s[1];
 933         fcntl(s[0], F_SETFD, FD_CLOEXEC);
 934         fcntl(s[0], F_SETFL, O_NONBLOCK);
 935         fcntl(s[1], F_SETFD, FD_CLOEXEC);
 936         fcntl(s[1], F_SETFL, O_NONBLOCK);
 937     }

# 觸發在init腳本文件中名字為early-boot和boot的action,並且執行其commands,其實是:on early-boot和on boot
948     action_for_each_trigger("early-boot", action_add_queue_tail);
 949     action_for_each_trigger("boot", action_add_queue_tail);
 950     drain_action_queue();
# 啟動所有屬性變化觸發命令,其實是: on property:ro.xx.xx=xx
953     queue_all_property_triggers();
 954     drain_action_queue();
# 進入死循環
987     for(;;) {

# 啟動所有init腳本中聲明的service,
# 如:266 service servicemanager /system/bin/servicemanager
#     user system
#     critical
#     onrestart restart zygote
#     onrestart restart media
994         restart_processes();
# 多路監聽設備管理,子進程運行狀態,屬性服務
1012         nr = poll(ufds, fd_count, timeout);
1013         if (nr <= 0)
1014             continue;
1016         if (ufds[2].revents == POLLIN) {
1017             /* we got a SIGCHLD - reap and restart as needed */
1018             read(signal_recv_fd, tmp, sizeof(tmp));
1019             while (!wait_for_one_process(0))
1020                 ;
1021             continue;
1022         }
1023
1024         if (ufds[0].revents == POLLIN)
1025             handle_device_fd(device_fd);
1026
1027         if (ufds[1].revents == POLLIN)
1028             handle_property_set_fd(property_set_fd);
1029         if (ufds[3].revents == POLLIN)
1030             handle_keychord(keychord_fd);
1031     }
1032
1033     return 0;
1034 }

 

二、Android init腳本語言

前面簡單分析了下init.c裡的操作,裡面提到了解析Init.rc和硬件腳本下面詳細解析下Android init腳本語言的規范。

Android初始化語言包含了四種類型的聲明:

    Actions(行為)、Commands(命令)、Services(服務)和Options(選項)

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


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

Actions(行為)
  Actions其實就是一序列的Commands(命令)。Actions都有一個trigger(觸發器),它被用於決定action的執行時間。當一個符合action觸發條件的事件發生時,action會被加入到執行隊列的末尾,除非它已經在隊列裡了。
    隊列中的每一個action都被依次提取出,而這個action中的每個command(命令)都將被依次執行。
Actions的形式如下:
        on <trigger>
           <command1>
           <command2>
           <command3>
on後面跟著一個觸發器,當trigger被觸發時,command1,command2,command3,會依次執行,直到下一個Action或下一個Service。
簡單來說,Actions就是Android在啟動時定義的一個啟動腳本,當條件滿足時,會執行該腳本,腳本裡都是一些命令commands,不同的腳本用on來區分。

Triggers(觸發器)
    Triggers(觸發器)是一個用於匹配特定事件類型的字符串,用於使Actions發生。
        boot:
            這是init執行後的第一個被觸發的Triggers(觸發器)。(在 /init.conf (啟動配置文件)被裝載之後)
        <name>=<value>:
            這種形式的Triggers(觸發器)會在屬性<name>被設置為指定的<value>時被觸發。
        device-added-<path>:
        device-removed-<path>:
            這種形式的Triggers(觸發器)會在一個設備節點文件被增刪時觸發。
        service-exited-<name>:
            這種形式的Triggers(觸發器)會在一個特定的服務退出時觸發。
觸發器通常和on一起來聯合使用。
示例:
on init
  export LD_LIBRARY_PATH /system/lib
  insmod modules/fsr.ko
  symlink /system/etc /etc
  mkdir /sdcard 0000 system system
  write /proc/cpu/alignment 4

on boot
  …
on property:ro.kernel.qemu=1
   start adbd
上面聲明了三個action:init,boot和一個屬性觸發器,當init被觸發時,會順序執行後面的命令,直到on boot新的action。Init的觸發是由init.c裡的函數action_for_each_trigger來決定的。當屬性ro.kernel.qemu為1 時,會觸發start adbd命令。


Services(服務)
Services(服務)是一個程序,它在初始化時啟動,並在退出時可選擇讓其重啟。Services(服務)的形式如下:
        service <name> <pathname> [ <argument> ]*
           <option>
           <option>
           ...
name:服務名
pathname:當前服務對應的程序位置
option:當前服務設置的選項

Options(選項)
    Options(選項)是一個Services(服務)的修正者。他們影響Services(服務)在何時,並以何種方式運行。
     critical:
            說明這是一個對於設備關鍵的服務。如果他四分鐘內退出大於四次,系統將會重啟並進入recovery(恢復)模式。

     disabled:
            說明這個服務不會同與他同trigger(觸發器)下的服務自動啟動。他必須被明確的按名啟動。

     setenv <name> <value> (設置環境變量)
            在進程啟動時將環境變量<name>設置為<value>。

     socket <name> <type> <perm> [ <user> [ <group> ] ]
            創建一個Uinx域的名為/dev/socket/<name> 的套接字,並傳遞它的文件描述符給已啟動的進程。<type> 必須是 "dgram"或"stream"。User 和 group默認為0。

     user <username>
            在啟動這個服務前改變該服務的用戶名。此時默認為root。(???有可能的話應該默認為nobody)。當前,如果你的進程要求Linux capabilities(能力),你無法使用這個命令。即使你是root,你也必須在程序中請求capabilities(能力)。然後降到你想要的 uid。

     group <groupname> [ <groupname> ]*
            在啟動這個服務前改變該服務的組名。除了(必需的)第一個組名,附加的組名通常被用於設置進程的補充組(通過setgroups())。此時默認為root。(???有可能的話應該默認為nobody)。
   
     oneshot
            服務退出時不重啟。

     class <name>
            指定一個服務類。所有同一類的服務可以同時啟動和停止。如果不通過class選項指定一個類,則默認為"default"類服務。

     onrestart
            當服務重啟,執行一個命令(下詳)。

Commands(命令)
    exec <path> [ <argument> ]*
         創建和執行一個程序(<path>)。在程序完全執行前,init將會阻塞。由於它不是內置命令,應盡量避免使用exec,它可能會引起init卡死。(??? 是否需要一個超時設置?)
    export <name> <value>
        在全局環境變量中設在環境變量 <name>為<value>。(這將會被所有在這命令之後運行的進程所繼承)
    ifup <interface>
        啟動網絡接口<interface>
    import <filename>
           解析一個init配置文件,擴展當前配置。
    hostname <name>
           設置主機名。
    chmod <octal-mode> <path>
           更改文件訪問權限。
    chown <owner> <group> <path>
           更改文件的所有者和組。
    class_start <serviceclass>
           啟動所有指定服務類下的未運行服務。
    class_stop <serviceclass>
        停止指定服務類下的所有已運行的服務。
    domainname <name>
           設置域名。
    insmod <path>
           加載<path>中的模塊。
    mkdir <path> [mode] [owner] [group]
           創建一個目錄<path>,可以選擇性地指定mode、owner以及group。如果沒有指定,默認的權限為755,並屬於root用戶和root組。
    mount <type> <device> <dir> [ <mountoption> ]*
        試圖在目錄<dir>掛載指定的設備。<device> 可以是以 [email protected] 的形式指定一個mtd塊設備。<mountoption>包括 "ro"、"rw"、"remount"、"noatime"、 ...
    setprop <name> <value>
           設置系統屬性 <name> 為 <value>值.
    setrlimit <resource> <cur> <max>
        設置<resource>的rlimit(資源限制)。
    start <service>
        啟動指定服務(如果此服務還未運行)。
    stop <service>
        停止指定服務(如果此服務在運行中)。
    symlink <target> <path>
        創建一個指向<path>的軟連接<target>。
    sysclktz <mins_west_of_gmt>
        設置系統時鐘基准(0代表時鐘滴答以格林威治平均時(GMT)為准)
    trigger <event>
           觸發一個事件。用於將一個action與另一個 action排列。
    write <path> <string> [ <string> ]*
           打開路徑為<path>的一個文件,並寫入一個或多個字符串。

Properties(屬性)
    Init更新一些系統屬性以提供對正在發生的事件的監控能力:
        init.action
               此屬性值為正在被執行的action的名字,如果沒有則為""。
        init.command
               此屬性值為正在被執行的command的名字,如果沒有則為""。
        init.svc.<name>
               名為<name>的service的狀態("stopped"(停止), "running"(運行), "restarting"(重啟))
 

 

三、Android init 示例

 

  1 on init
  2
  3 sysclktz 0
  4
  5 loglevel 3
  6
  7 # setup the global environment
  8     export PATH /system/busybox/bin:/system/busybox/sbin:/system/busybox/usr/bin:/system/busybox/usr/sbin:/sbin:/system/sbin:/system/bin:/system/xbin
  9     export LD_LIBRARY_PATH /system/lib
 10     export ANDROID_BOOTLOGO 1
 11     export ANDROID_ROOT /system
 12     export ANDROID_ASSETS /system/app
 13     export ANDROID_DATA /data
 14     export EXTERNAL_STORAGE /sdcard

... ...

 23     symlink /system/etc /etc
 24     symlink /sys/kernel/debug /d
 25    
 26 # create mountpoints and mount tmpfs on sqlite_stmt_journals
 27     mkdir /sdcard 0000 system system
 28     mkdir /system
 29     mkdir /data 0771 system system
 30     mkdir /cache 0770 system cache
 31     mkdir /config 0500 root root
 32     mkdir /sqlite_stmt_journals 01777 root root
 33     mount tmpfs tmpfs /sqlite_stmt_journals size=4m
 34
 35 #    mount rootfs rootfs / ro remount
 36
 37     write /proc/sys/kernel/panic_on_oops 1
 38     write /proc/sys/kernel/hung_task_timeout_secs 0
 39     write /proc/cpu/alignment 4
 40     write /proc/sys/kernel/sched_latency_ns 10000000
 41     write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
 42     write /proc/sys/kernel/sched_compat_yield 1
 43     write /proc/sys/kernel/sched_child_runs_first 0
 44
 45 # Create cgroup mount points for process groups
 46     mkdir /dev/cpuctl

 47     mount cgroup none /dev/cpuctl cpu
 48     chown system system /dev/cpuctl
 49     chown system system /dev/cpuctl/tasks
 50     chmod 0777 /dev/cpuctl/tasks
 51     write /dev/cpuctl/cpu.shares 1024
 52
 53     mkdir /dev/cpuctl/fg_boost
 54     chown system system /dev/cpuctl/fg_boost/tasks
 55     chmod 0777 /dev/cpuctl/fg_boost/tasks
 56     write /dev/cpuctl/fg_boost/cpu.shares 1024
 57
 58     mkdir /dev/cpuctl/bg_non_interactive
 59     chown system system /dev/cpuctl/bg_non_interactive/tasks
 60     chmod 0777 /dev/cpuctl/bg_non_interactive/tasks
 61     # 5.0 %
 62     write /dev/cpuctl/bg_non_interactive/cpu.shares 52
 ... ...

146 on boot
147 # basic network init
148     ifup lo
149     hostname localhost
150     domainname localdomain
151
152 # set RLIMIT_NICE to allow priorities from 19 to -20
153     setrlimit 13 40 40
154
155 # Define the oom_adj values for the classes of processes that can be
156 # killed by the kernel.  These are used in ActivityManagerService.
157     setprop ro.FOREGROUND_APP_ADJ 0
158     setprop ro.VISIBLE_APP_ADJ 1
159     setprop ro.SECONDARY_SERVER_ADJ 2
160     setprop ro.BACKUP_APP_ADJ 2
161     setprop ro.HOME_APP_ADJ 4
162     setprop ro.HIDDEN_APP_MIN_ADJ 7

... ...

233 # Define TCP buffer sizes for various networks
234 #   ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax,
235     setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208
236     setprop net.tcp.buffersize.wifi    4095,87380,110208,4096,16384,110208
237     setprop net.tcp.buffersize.umts    4094,87380,110208,4096,16384,110208
238     setprop net.tcp.buffersize.edge    4093,26280,35040,4096,16384,35040
239     setprop net.tcp.buffersize.gprs    4092,8760,11680,4096,8760,11680
240     setprop net.dns1 8.8.8.8
241     setprop net.eth0.dns1 8.8.8.8
242
243     class_start default

... ...

247 service console /system/bin/sh
248    console
249

252 service adbd /sbin/adbd
253     disabled'

259 on property:persist.service.adb.enable=1
260     start adbd
261
262 on property:persist.service.adb.enable=0
263     stop adbd

265 service servicemanager /system/bin/servicemanager
266     user system
267     critical
268     onrestart restart zygote
269     onrestart restart media

271 service vold /system/bin/vold
272     socket vold stream 0660 root mount

 

282 service ril-daemon /system/bin/rild
283     socket rild stream 660 root radio
284     socket rild-debug stream 660 radio system
285     user root
286     group radio cache inet misc
287
288 service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
289     socket zygote stream 666
290     onrestart write /sys/android_power/request_state wake
291     onrestart write /sys/power/state on
292     onrestart restart media

293
294 service media /system/bin/mediaserver
295     user media
296     group system audio camera graphics inet net_bt net_bt_admin
297
298 service bootsound /system/bin/playmp3
299     user media
300     group audio
301     oneshot
302
303 service bootanim /system/bin/bootanimation
304     user graphics
305     group graphics

306     disabled
307     oneshot

... ...

由上面的例子可知:該init.rc裡定義了4個actions:

    on init

    on boot

    on property:persist.service.adb.enable=1
    on property:persist.service.adb.enable=0
   其中,前兩個的觸發器是由Init.c裡的代碼action_for_each_trigger實現觸發的,後兩個是由屬性的值發生改變時觸發的。當他們被觸發時,會依次執行其後面的值,直到遇到一個新的Action或service。

該init.rc中定義了9個services,這些service屬於本地服務,它們都是由c或c++代碼寫的,以可執行文件的方式存在文件系統裡的/system/bin或/sbin目錄下。由前面知識可知,如果一個服務沒有使用class選項指定其分類,則其默認分類為:default,那麼這9個services的分類為default。在on boot action觸發時,其最後一個命令是class_start default,也就是將這9個services都依次啟動。

init.rc中的9個service裡,以zygote和servicemanager最為重要。

1> zygote service

該service主要用於創建Dalvik Java虛擬機,然後啟動SystemServer,啟動所有的Android服務,最終啟動Android系統。

2> servicemanager

該service主要用來打開binder驅動,binder是Android Service通信機制的底層實現,在該service裡封裝了binder的操作接口。

技巧:

  在調試或理解系統的工作原理的時候,我們經常要去找服務程序對應的源碼。

  尋找c或c++程序的源碼文件:

  例如:以尋找init程序對應的源碼為例。

   find ./ -name Android.mk -exec grep -l init {} \;

  注:通過find命令查找所有的Android.mk, 通過grep從中查找程序字符串,得到其路徑,然後去路徑下找源碼即可,這麼做的原因是,c或c++代碼都是通過Android.mk來指導編譯的。

   尋找java源碼文件:

   java源碼的特點是和類名一致,所以如果我們知道一個類名,找其java源碼就直接加上java後綴即可。

  例如:尋找com.android.internal.os.ZygoteInit類的代碼。

   find ./ -name ZygoteInit.java

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved