Android init進程啟動


一、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> 可以是以 mtd@name 的形式指定一個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"(重啟))

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM