Android init

轉載:http://www.cnblogs.com/linucos/archive/2012/05/22/2513046.html

1. init程序邏輯

文章引用地址:一篇pdf文檔,來自百度文庫

Android 源碼分析 -- 
(一) Android 啓動過程 
[email protected] 
1. 源碼文件路徑: 
platform/system/core/init/init.c 
0) 這個代碼文件主要用於實現 Android 系統中 init 進程 (init 進程爲 Android 系統中用 
戶空間啓動的第一個進程,其作用相當於 Linux 系統中的 init 進程) 
NOTE: 如果調用此文件生成的可執行文件的第一個參數爲"ueventd", 
那麼此文件 
將實現 Android 系統中的 "ueventd" 進程。 
1) 在進行編譯後,此文件生成的可執行程序名稱爲"init",同時會生成一個指向此文 
件的軟鏈接: "ueventd" 
int main(int argc, char **argv) 
{
2) 基於 C 語言的風格,在函數入口處聲明一些後續會使用的變量。 
int fd_count = 0; 
struct pollfd ufds[4]; 
char *tmpdev; 
char* debuggable; 
char tmp[32]; 
int property_set_fd_init = 0; 
int signal_fd_init = 0; 
int keychord_fd_init = 0; 
3) 如果執行此文件生成的可執行程序的方式類似於: "ueventd xxx"。也即是基於執行 
此 文 件 對 應 的 軟 鏈 接 : /sbin/ueventd 時 會 調 用 "ueventd_main" 函 數 , 進 而 生 
成"ueventd" 進程。 
4) Android 系統中的 ueventd 進程用於實現用戶態進程與內核進行數據通信。 
5) 在 Android 系統的 init.rc 文件: platform/system/core/rootdir/init.rc 中通過如下方式 
來啓動 ueventd 進程: 
on early-init 
# Set init and its forked children's oom_adj. 
write /proc/1/oom_adj -16 
start ueventd 
if (!strcmp(basename(argv[0]), "ueventd")) 

return ueventd_main(argc, argv); 
6) 
生成 Android 系統中的一些基本的系統目錄並掛載對應的文件系統。 
/* clear the umask */ 
umask(0); 
/* Get the basic filesystem setup we need put 
* together in the initramdisk on / and then we'll 
* let the rc file figure out the rest. 
*/ 
mkdir("/dev", 0755); 
mkdir("/proc", 0755); 
mkdir("/sys", 0755); 
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "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); 
/* indicate that booting is in progress to background fw loaders, etc 
*/ 
close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000)); 
/* We must have some place other than / to create the 
* device nodes for kmsg and null, otherwise we won't 
* be able to remount / read-only later on. 
* Now that tmpfs is mounted on /dev, we can actually 
* talk to the outside world. 
*/ 
7) 生成 "/dev/__null__" 虛擬設備(類似於 Linux 系統中的 /dev/null 設備)並將 
stdin/stdout/stderr 三個文件重定向到 "/dev/__null__" 
open_devnull_stdio(); 
8) 
生成 " /dev/__kmsg__" 虛擬設備用於記錄 log。 
Klog_init 實現文件: system/core//libcutils/klog.c 
klog_init(); 
INFO("reading config file\n"); 
9) 
解析 init.rc (platform/system/core/rootdir/init.rc)。 

init_parse_config_file("/init.rc"); 
/* pull the kernel commandline and ramdisk properties file in */ 
10) 從 "/proc/cmdline" 中讀取內核命令行參數, 
對應函數實現路徑: platform/system/core/init/util.c 
import_kernel_cmdline(0, import_kernel_nv); 
11) 在第 10 步讀取完 /proc/cmdline 中的參數後,修改此文件的權限,禁止非授權 
用戶操作此文件。 
/* don't expose the raw commandline to nonpriv processes */ 
chmod("/proc/cmdline", 0440); 
12) 從 "/proc/cpuinfo" 中讀取系統的 CPU 硬件信息。 
對應函數實現路徑: platform/system/core/init/util.c 
get_hardware_name(hardware, &revision); 
13) 基於第 12 步中讀取的硬件信息來解析特定於硬件的配置信息。 
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); 
init_parse_config_file(tmp); 
14) 基 於 "early-init","property_init","keychord_init","console_init" 標 識 , 使 用 " 
queue_builtin_action"來過濾上述操作中解析的 init.rc 文件中的 action, 
並將符合 
條 件 的 action 添 加 到 對 應 的 
action_queue 中 , 然 後 調 用 " 
action_for_each_trigger"來運行這些 actions(實際上是 action 在 list 中的分類移 
動操作)。 
對應函數實現路徑: platform/system/core/init/init_parser.c 
基於下面的運行邏輯可以看出,運行"init.rc"中的 action 的順序爲: 
"early-init" -> "init" -> "early-boot" -> "boot" 
action_for_each_trigger("early-init", action_add_queue_tail); 
queue_builtin_action(wait_for_coldboot_done_action, 
"wait_for_coldboot_done"); 
queue_builtin_action(property_init_action, "property_init"); 
queue_builtin_action(keychord_init_action, "keychord_init"); 
queue_builtin_action(console_init_action, "console_init"); 
queue_builtin_action(set_init_properties_action, 
"set_init_properties"); 
/* execute all the boot actions to get us started */ 
action_for_each_trigger("init", action_add_queue_tail); 

/* skip mounting filesystems in charger mode */ 
if (strcmp(bootmode, "charger") != 0) 
{
action_for_each_trigger("early-fs", action_add_queue_tail); 
action_for_each_trigger("fs", action_add_queue_tail); 
action_for_each_trigger("post-fs", action_add_queue_tail); 
action_for_each_trigger("post-fs-data", action_add_queue_tail); 
}
queue_builtin_action(property_service_init_action, 
"property_service_init"); 
queue_builtin_action(signal_init_action, "signal_init"); 
queue_builtin_action(check_startup_action, "check_startup"); 
if (!strcmp(bootmode, "charger")) 
{
action_for_each_trigger("charger", action_add_queue_tail); 
}
else 
{
action_for_each_trigger("early-boot", action_add_queue_tail); 
action_for_each_trigger("boot", action_add_queue_tail); 
}
/* run all property triggers based on current state of the properties 
*/ 
queue_builtin_action(queue_property_triggers_action, 
"queue_propety_triggers"); 
#if BOOTCHART 
queue_builtin_action(bootchart_init_action, "bootchart_init"); 
#endif 
15) "init" 進程開始進行"循環事件處理"邏輯。 
for (;;) 
{
int nr, i, timeout = -1; 
16) 運行第 14 步操作中所分類整理的 action_queue 中對應的 action。 
execute_one_command(); 
17) 查看當前的服務進程狀態,如果有服務進程退出,重啓對應服務進程。 
restart_processes(); 

18) 基於 property_service 的事件句柄填充 poll event 結構體,用於後續 poll 操作。 
if (!property_set_fd_init && get_property_set_fd() > 0) 
{
ufds[fd_count].fd = get_property_set_fd(); 
ufds[fd_count].events = POLLIN; 
ufds[fd_count].revents = 0; 
fd_count++; 
property_set_fd_init = 1; 
}
19) 處理當前 init 進程上接收到的 signal,主要是處理 SIGCHLD。 
if (!signal_fd_init && get_signal_fd() > 0) 
{
ufds[fd_count].fd = get_signal_fd(); 
ufds[fd_count].events = POLLIN; 
ufds[fd_count].revents = 0; 
fd_count++; 
signal_fd_init = 1; 
}
20) 基於 keychord service 的事件句柄填充 poll event 結構體,用於後續 poll 操作。 
if (!keychord_fd_init && get_keychord_fd() > 0) 
{
ufds[fd_count].fd = get_keychord_fd(); 
ufds[fd_count].events = POLLIN; 
ufds[fd_count].revents = 0; 
fd_count++; 
keychord_fd_init = 1; 
}
21) 如果有所監控的子進程退出,此時 init 進程需要負責重新啓動這些退出的服務進 
程,因此下面調用 poll 時的 timeout 設置爲 0,這樣就不會因爲 poll 在無事件激發時而 
阻塞導致當前的 init 進程對"重啓服務進程"處理的的延遲,從而可以儘快恢復退出的 
服務進程。 
if (process_needs_restart) 
{
timeout = (process_needs_restart - gettime()) * 1000; 
if (timeout < 0) 
timeout = 0; 
}
22) 如果當前的 action_queue 中有需要處理的 action, 麼下面調用 poll 時的 timeout 

設置爲 0,這樣就不會因爲 poll 在無事件激發時而阻塞導致當前的 init 進程對 action 
處理的的延遲,從而提高 init 進程對 action 處理的實時性。 

if (!action_queue_empty() || cur_action) 
timeout = 0; 
23) 關於" BOOTCHART"參數的解釋: 
/* 
* 如果在編譯選項中添加了 BOOTCHART 參數,那麼意味着在系統的啓動 
* 過程中需要生成 bootchart(http://www.bootchart.org/),用於後續 
* Android 啓動過程中的性能分析並生成系統啓動過程的可視圖表。 
* makefile 中的編譯選項如下: 
ifeq ($(strip $(INIT_BOOTCHART)),true) 
LOCAL_SRC_FILES += bootchart.c 
LOCAL_CFLAGS 
endif 
+= -DBOOTCHART=1 
bootchart 的實現文件: platform/system/core/init/bootchart.c 
*
#if BOOTCHART 
if (bootchart_count > 0) 
{
if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) 
timeout = BOOTCHART_POLLING_MS; 
if (bootchart_step() < 0 || --bootchart_count == 0) 
{
bootchart_finish(); 
bootchart_count = 0; 
}
}
#endif 
24) 類似於網絡服務器開發中常見的基於"poll"機制來檢測所關注的句柄上是否有指定 
的事件激發。 
nr = poll(ufds, fd_count, timeout); 
if (nr <= 0) 
continue; 
for (i = 0; i < fd_count; i++) 
{
25) 如果當前所關注的事件句柄上有事件發生,進行對應的事件處理。 
if (ufds[i].revents == POLLIN) 
{
if (ufds[i].fd == get_property_set_fd()) 

handle_property_set_fd(); 
else if (ufds[i].fd == get_keychord_fd()) 
handle_keychord(); 
else if (ufds[i].fd == get_signal_fd()) 
handle_signal(); 
}
}
}
return 0; 
}

2. init.rc類配置文件解析

文章引用地址:一篇pdf文檔,來自百度文庫

Android 的初始化語言腳本 init.rc 解析 
Android init.rc (Android init language) 
Android 初始化語言由四大類聲明組成:行爲類(Actions),命令類(Commands),服務類
(Services),選項類(Options)。 
1)   初始化語言以行爲單位,由以空格間隔的語言符號組成。C 風格的反斜槓轉義符可以
用來插入空白到語言符號。雙引號也可以用來防止文本被空格分成多個語言符號。當
反斜槓在行末時,作爲折行符。 
2)   以#開始(前面允許有空格)的行爲註釋行。 
3)   Actions 和 Services 隱含聲明一個新的段落。所有該段落下 Commands 或 Options 的
聲明屬於該段落。第一段落前的 Commands 或 Options 被忽略。 
4)   Actions 和 Services 擁有獨一無二的命名。在它們之後聲明相同命名的類將被當作
錯誤並忽略。 
 
行爲類(Actions) 
Actions 是一系列命令的命名。 
Actions 擁有一個觸發器(trigger)用來決定 action 何時執行。當一個 action 在符合觸發
條件被執行時,如果它還沒被加入到待執行隊列中的話,則加入到隊列最後。 
隊列中的 action 依次執行,action 中的命令也依次執行。Init 在執行命令的中間處理其它
活動(設備創建/銷燬,property 設置,進程重啓)。 
Actions 表現形式爲: 
on <trigger> 
   <command> 
   <command> 
   <command> 
 
服務類(Services) 
Services 是由 init 啓動,在它們退出時重啓(可選)。 
Service 表現形式爲: 
service <name> <pathname> [ <argument> ]* 
   <option> 
   <option> 
   ... 
   
選項類(Options) 
Options 是 Services 的修飾,它們影響 init 何時、如何運行 service. 
PDF 文件使用 "pdfFactory Pro" 試用版本創建 www.fineprint.cn
critical 
     這是一個設備關鍵服務(device-critical service) .如果它在 4 分鐘內退出超過 4
次,設備將重啓並進入恢復模式。 
 
disabled 
     這個服務的級別將不會自動啓動,它必須被依照服務名指定啓動纔可以啓動。 
 
setenv <name> <value> 
     設置已啓動的進程的環境變量<name>的值<value> 
 
socket <name> <type> <perm> [ <user> [ <group> ] ] 
     創建一個名爲/dev/socket/<name>的 unix domin socket,並傳送它的 fd 到已啓動的
進程。<type>必須爲"dgram"或"stream".用戶和組默認爲 0. 
 
user <username> 
     在執行服務前改變用戶名。當前默認爲 root.如果你的進程需要 linux 能力,你不能
使用這個命令。你必須在還是 root 時請求能力,並下降到你需要的 uid. 
 
group <groupname> [ <groupname> ]* 
     在執行服務前改變組。在第一個組後的組將設爲進程附加組(通過 setgroups()).當前
默認爲 root. 
 
oneshot 
     在服務退出後不重啓。 
 
class <name> 
     爲 service 指定一個類別名。同樣類名的所有的服務可以一起啓動或停止。如果沒有
指定類別的服務默認爲"default"類。 
 
onrestart 
       當服務重啓時執行一個命令。 
 
PDF 文件使用 "pdfFactory Pro" 試用版本創建 www.fineprint.cn
Triggers 
     Triggers(觸發器)是一個字符串,可以用來匹配某種類型的事件並執行一個 action。 
 
boot 
     這是當 init 開始後執行的第一個觸發器(當/init.conf 被加載) 
 
<name>=<value> 
     當 property <name>被設爲指定的值<value>時觸發。 
 
device-added-<path> 
device-removed-<path> 
     當設備節點被添加或移除時觸發。 
 
service-exited-<name> 
     當指定的服務存在時觸發 
 
 
命令類(Commands) 
exec <path> [ <argument> ]* 
     Fork 並執行一個程序(<path>).這將被 block 直到程序執行完畢。最好避免執行例如
內建命令以外的程序,它可能會導致 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>創建一個目錄,可選選項:mod,owner,group.如果沒有指定,目錄以 755 權限,
owner 爲 root,group 爲 root 創建. 
 
mount <type> <device> <dir> [ <mountoption> ]* 
     嘗試 mount <device>到目錄<dir>. <device>可以用 mtd@name格式以命名指定一個 mtd
塊設備。<mountoption>包含"ro","rw","remount","noatime". 
 
setkey 
PDF 文件使用 "pdfFactory Pro" 試用版本創建 www.fineprint.cn
     暫時沒有 
 
setprop <name> <value> 
     設置系統 property <name>的值<value>. 
 
setrlimit <resource> <cur> <max> 
     設置 resource 的 rlimit. 
 
start <service> 
     啓動一個沒有運行的服務。 
 
stop <service> 
     停止一個正在運行的服務。 
 
symlink <target> <path> 
     創建一個<path>的符號鏈接到<target> 
 
sysclktz <mins_west_of_gmt> 
     設置系統時區(GMT 爲 0) 
 
trigger <event> 
     觸發一個事件。用於調用其它 action。 
 
write <path> <string> [ <string> ]* 
     打開<path>的文件並寫入一個或多個字符串。 
 
Properties 
Init 會更新一些系統 property 以提供查看它正在幹嘛。 
init.action 
     當前正在執行的 action,如果沒有則爲"" 
PDF 文件使用 "pdfFactory Pro" 試用版本創建 www.fineprint.cn
 
init.command 
     被執行的命令,如果沒有則爲"" 
 
init.svc.<name> 
     命名爲<name>的服務的狀態("stopped", "running", "restarting") 
 
init.rc 示例: 
 
# not complete -- just providing some examples of usage 

on boot 
   export PATH /sbin:/system/sbin:/system/bin 
   export LD_LIBRARY_PATH /system/lib 
 
   mkdir /dev 
   mkdir /proc 
   mkdir /sys 
  
   mount tmpfs tmpfs /dev 
   mkdir /dev/pts 
   mkdir /dev/socket 
   mount devpts devpts /dev/pts 
   mount proc proc /proc 
   mount sysfs sysfs /sys 
  
   write /proc/cpu/alignment 4 
  
   ifup lo 
  
   hostname localhost 
   domainname localhost 
  
   mount yaffs2 mtd@system /system 
 
   mount yaffs2 mtd@userdata /data 
  
   import /system/etc/init.conf 
  
   class_start default 
  
service adbd /sbin/adbd 
   user adb 
   group adb 
  
service usbd /system/bin/usbd –r 
   user usbd 
   group usbd 
   socket usbd 666 
  
service zygote /system/bin/app_process -Xzygote /system/bin –zygote 
   socket zygote 666 
  
service runtime /system/bin/runtime 
   user system 
   group system 
  
on device-added-/dev/compass 
   start akmd 
  
on device-removed-/dev/compass 
   stop akmd 
  

# 根據全局屬性值來開啓一個service

on property:persist.service.XXX.enable=1

    restart XXX
on property:persist.service.XXX.enable=0
    stop XXX


service akmd /sbin/akmd 
   disabled 
   user akmd 
   group akmd 
  
調試 
默認情況下,init 執行的程序輸出的信息和錯誤到/dev/null.爲了 debug,你可以通過
Android 程序 logwrapper 執行你的程序。這將復位向輸出/錯誤輸出到 Android logging 系
統(通過 logcat 訪問)。 
例如 
service akmd /system/bin/logwrapper /sbin/akmd


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