init進程是Android系統用戶空間中的第一個進程,其進程號也是1,足見其重要性。所以它的責任也是重大的,概括地來說init進程主要做了以下幾件事:
- 作為守護進程
- 解析和執行init.rc文件
- 屬性服務
- 生成設備驅動節點
接下來文章就著init進程的源碼,來一個個分析init進程的工作。首先來看看init進程的源碼:
int main(int argc, char **argv)
{
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;
bool is_charger = false;
//啟動ueventd
if (!strcmp(basename(argv[0]), "ueventd"))
return ueventd_main(argc, argv);
//啟動watchdogd
if (!strcmp(basename(argv[0]), "watchdogd"))
return watchdogd_main(argc, argv);
/* 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));//檢測/dev/.booting文件是否可讀寫和創建
/* 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.
*/
open_devnull_stdio();//重定向標準輸入/輸出/錯誤輸出到/dev/_null_
klog_init();//log初始化
property_init();//屬性服務初始化
//從/proc/cpuinfo中讀取Hardware名,在后面的mix_hwrng_into_linux_rng_action函數中會將hardware的值設置給屬性ro.hardware
get_hardware_name(hardware, &revision);
//導入并設置內核變量
process_kernel_cmdline();
//selinux相關,暫不分析
union selinux_callback cb;
cb.func_log = klog_write;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize();
/* These directories were necessarily created before initial policy load
* and therefore need their security context restored to the proper value.
* This must happen before /dev is populated by ueventd.
*/
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
is_charger = !strcmp(bootmode, "charger");//關機充電相關,暫不做分析
INFO("property init\n");
if (!is_charger)
property_load_boot_defaults();
INFO("reading config file\n");
init_parse_config_file("/init.rc");//解析init.rc配置文件
/*
* 解析完init.rc后會得到一系列的action等,下面的代碼將執行處于early-init階段的action。
* init將action按照執行時間段的不同分為early-init、init、early-boot、boot。
* 進行這樣的劃分是由于有些動作之間具有依賴關系,某些動作只有在其他動作完成后才能執行,所以就有了先后的區別。
* 具體哪些動作屬于哪個階段是在init.rc中的配置決定的
*/
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(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
/* 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 (!is_charger) {
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);
}
/* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
* wasn't ready immediately after wait_for_coldboot_done
*/
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
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 (is_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_property_triggers");
#if BOOTCHART
queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
for(;;) {//init進入無限循環
int nr, i, timeout = -1;
//檢查action_queue列表是否為空。如果不為空則移除并執行列表頭中的action
execute_one_command();
restart_processes();//重啟已經死去的進程
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;
}
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;
}
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;
}
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (!action_queue_empty() || cur_action)
timeout = 0;
#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
//等待事件發生
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
for (i = 0; i < fd_count; i++) {
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())//處理keychord事件
handle_keychord();
else if (ufds[i].fd == get_signal_fd())//處理
handle_signal();//處理SIGCHLD信號
}
}
}
return 0;
}
1.文件掛載,生成驅動節點
首先來看看main函數第一塊比較重要的部分,文件掛載:
/* 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));//檢測/dev/.booting文件是否可讀寫和創建
/* 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.
*/
open_devnull_stdio();//重定向標準輸入/輸出/錯誤輸出到/dev/_null_
klog_init();//log初始化
這部分代碼還是比較簡單的,就是掛載一些文件的掛載。值得說明的是:
1.tmpfs是一種虛擬內存文件系統,通常情況下該文件系統是常駐ram的,所以其讀取速度要高于內存和磁盤。而/dev目錄則存放了訪問硬件設備所需的驅動程序文件,將tmpfs作用于驅動目錄最主要的原因還是提高硬件設備的訪問速度。
- devpts是一種虛擬終端文件系統。
- proc是一個基于內存的文件系統,其主要的作用是完成內核與應用之間的數據交換。
- sysfs是一種特殊的文件系統,在Linux 2.6中引入,用于將系統中的設備組織成層次結構,并向用戶模式程序提供詳細的內核數據結構信息,將proc、devpts、devfs三種文件系統統一起來。
- /dev /proc /sysfs 等等目錄都是系統運行是目錄,在Android系統編譯時是不存在的,它們都是由init進程創建的。當系統終止時他們也會消失。
最后構造的目錄如下:
另外,open_devnull_stdio()函數的作用是重定向標準輸入/輸出/錯誤輸出到/dev/null。klog_init()用于初始化log,通過其實現可以看出log被打印到/dev/kmsg文件中。主要在代碼中最后通過fcntl和unlink使得/dev/kmsg不可被訪問,這就保證了只有log程序才可以訪問。
2. 解析和執行init.rc文件
init.rc文件定義了在系統啟動時需要執行的動作也需要啟動的服務,這些服務對于整個Android系統的正常運行都是至關重要的,其中大家熟知的ZygoteService就是在init.rc文件中指定需要執行的。所以首先來看看init.rc文件的語法。
2.1 init.rc文件介紹
init.rc文件并不是普通的配置文件,而是由一種被稱為“Android初始化語言”(Android Init Language,這里簡稱為AIL)的腳本寫成的文件。首先來看一下init.rc文件
#筆者注:引入其他rc文件,這部分文件也將被執行
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.trace.rc
on early-init
# Set init and its forked children's oom_adj
write /proc/1/oom_adj -16
on init
#動作列表
mkdir /system
mkdir /data 0771 system system
mkdir /cache 0770 system cache
mkdir /config 0500 root root
on boot
...
on charger
class_start charger
on property:vold.decrypt=trigger_reset_main
class_reset main
service evened /sbin/ueventd
service health /sbin/healthd
...
#zygote service
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \
--start-system-server
socketzygote stream 666 #socket關鍵字表示OPTION
onrestart write /sys/android_power/request_state wake #onrestart也是OPTION
onrestart write /sys/power/state on
onrestart restart media
以上就是init.rc文件的節選。下面先來介紹一下其對應的語法。AIL由如下4部分組成。
- 動作(Actions)
- 命令(Commands)
- 服務(Services)
- 選項(Options)
AIL在編寫時需要分成多個部分(Section),而每一部分的開頭需要指定Actions或Services。也就是說,每一個Actions或Services確定一個Section。而所有的Commands和Options只能屬于最近定義的Section。如果Commands和Options在第一個Section之前被定義,它們將被忽略。
Actions和Services的名稱必須唯一。如果有兩個或多個Action或Service擁有同樣的名稱,那么init在執行它們時將拋出錯誤,并忽略這些Action和Service。
Actions的語法格式如下:
on trigger
command
command
command
所以action是以on開頭的,init.rc中定義的early-init,init,boot,early-boot都是action。comman的就是這個action需要執行的命令。這些指令很好理解,都是Linux下的指令。以init為例,在這個階段創建了幾個文件,并且設置了對應的權限和user。
on init
#動作列表
mkdir /system
mkdir /data 0771 system system
mkdir /cache 0770 system cache
mkdir /config 0500 root root
接下來再來看看service的結構。
service [name] [pathname] [ argument ]*
[option]
[option]
比如servicemanager
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
Services的選項是服務的修飾符,可以影響服務如何以及怎樣運行。服務支持的選項如下:
- critical
表明這是一個非常重要的服務。如果該服務4分鐘內退出大于4次,系統將會重啟并進入 Recovery (恢復)模式。 - disabled
表明這個服務不會同與他同trigger (觸發器)下的服務自動啟動。該服務必須被明確的按名啟動。 - setenv [name] [value]
在進程啟動時將環境變量[name]設置為[value]。 - socket [name][type] [perm] [ user [ group ] ]
創建一個unix域的名為/dev/socket/name 的套接字,并傳遞它的文件描述符給已啟動的進程。type 必須是 "dgram","stream" 或"seqpacket"。用戶和組默認是0。 - user username
在啟動這個服務前改變該服務的用戶名。此時默認為 root。 - group groupname [groupname ]*
在啟動這個服務前改變該服務的組名。除了(必需的)第一個組名,附加的組名通常被用于設置進程的補充組(通過setgroups函數),檔案默認是root。 - oneshot
服務退出時不重啟。 - class name
指定一個服務類。所有同一類的服務可以同時啟動和停止。如果不通過class選項指定一個類,則默認為"default"類服務。 - onrestart
當服務重啟,執行一個命令
2.2 init.rc文件解析
接下來正式進入到init.rc文件的解析過程,在init程序中有這樣一行代碼:
init_parse_config_file("/init.rc");//解析init.rc配置文件
其對應的實現是
int init_parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);
if (!data) return -1;
parse_config(fn, data);
DUMP();
return 0;
}
接下來再來看看parse_config
static void parse_config(const char *fn, char *s)
{
struct parse_state state;
struct listnode import_list;//導入鏈表,用于保持在init.rc中通過import導入的其他rc文件
struct listnode *node;
char *args[INIT_PARSER_MAXARGS];
int nargs;
nargs = 0;
state.filename = fn;//初始化filename的值為init.rc文件
state.line = 0;//初始化行號為0
state.ptr = s;//初始化ptr指向s,即read_file讀入到內存中的init.rc文件的首地址
state.nexttoken = 0;//初始化nexttoken的值為0
state.parse_line = parse_line_no_op;//初始化行解析函數
list_init(&import_list);
state.priv = &import_list;
for (;;) {
switch (next_token(&state)) {
case T_EOF://如果返回為T_EOF,表示init.rc已經解析完成,則跳到parser_done解析import進來的其他rc腳本
state.parse_line(&state, 0, 0);
goto parser_done;
case T_NEWLINE:
state.line++;//一行讀取完成后,行號加1
if (nargs) {//如果剛才解析的一行為語法行(非注釋等),則nargs的值不為0,需要對這一行進行語法解析
int kw = lookup_keyword(args[0]);//init.rc中每一個語法行均是以一個keyword開頭的,因此args[0]即表示這一行的keyword
if (kw_is(kw, SECTION)) {
state.parse_line(&state, 0, 0);
parse_new_section(&state, kw, nargs, args);
} else {
state.parse_line(&state, nargs, args);
}
nargs = 0;//復位
}
break;
case T_TEXT://將nexttoken解析的一個text保存到args字符串數組中,nargs的最大值為INIT_PARSER_MAXARGS(64),即init.rc中一行最多不能超過INIT_PARSER_MAXARGS個text(單詞)
if (nargs < INIT_PARSER_MAXARGS) {
args[nargs++] = state.text;
}
break;
}
}
......
}
代碼的總體邏輯比較簡單,解析文件中的關鍵字、換行符等等解析各個屬性,然后添加到對應的section中。后續代碼邏輯也比較信息,讀者可以自行看源碼,這里因為篇幅原因就不再繼續介紹源碼了。
解析init.rc文件的最終效果就是解析文件中的各個action和service,然后將其添加到action_list和service_list中。供后續執行list中的action和service。
而在解析完成后,有這樣一段代碼:
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(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
/* 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 (!is_charger) {
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);
}
/* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
* wasn't ready immediately after wait_for_coldboot_done
*/
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
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 (is_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_property_triggers");
#if BOOTCHART
queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
這部分代碼就比較好理解了,執行已經解析到的service和action。
2.3 Zygote service的啟動
看了以上代碼可能大家會奇怪,對應執行的都是action,怎么沒有看到service的執行,其實service的啟動已經包含在了action的執行當中了。以Zygote為例,這個service的啟動就是放在boot過程中的。接下來重點講一下。
在init.rc下的action中有一個command叫做
class_start [classservice]
指令的意思就是啟動屬于classservice這個類別的所有service。而在boot階段就有一個command是:
start_service default
所以在boot階段會啟動所有的default的service。或許你已經猜到了,沒錯,Zygote就屬于default類型,因而在boot階段會被執行。關于Zygote如何被設置為default,具體的工作在parse_service階段完成的,讀者可以自行分析源碼,這里不做介紹。
相應的啟動服務的操作也比較簡單,在init進程中通過fork,創建一個新的進程,用于運行Zygote,相關代碼如下:
if(pid == 0) {
//pid為零,我們在子進程中
struct socketinfo *si;
struct svcenvinfo *ei;
char tmp[32];
int fd, sz;
//得到屬性存儲空間的信息并加到環境變量中,后面在屬性服務一節中會碰到使用它的地方。
get_property_workspace(&fd, &sz);
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
//添加環境變量信息
for (ei = svc->envvars; ei; ei = ei->next)
add_environment(ei->name, ei->value);
//根據socketinfo創建socket
for (si = svc->sockets; si; si = si->next) {
int s = create_socket(si->name,
!strcmp(si->type,"dgram") ?
SOCK_DGRAM :SOCK_STREAM,
si->perm,si->uid, si->gid);
if (s >= 0) {
//在環境變量中添加socket信息。
publish_socket(si->name, s);
}
}
......//設置uid,gid等
setpgid(0, getpid());
if(!dynamic_args) {
/*
執行/system/bin/app_process,這樣就進入到app_process的main函數中了。
fork、execve這兩個函數都是Linux系統上常用的系統調用。
*/
if (execve(svc->args[0], (char**)svc->args, (char**) ENV) < 0) {
......
}
}else {
......
}
為什么要執行/system/bin/app_process?再來看看Zygote在init.rc文件中的定義:
#zygote service
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \
--start-system-server
socketzygote stream 666 #socket關鍵字表示OPTION
onrestart write /sys/android_power/request_state wake #onrestart也是OPTION
onrestart write /sys/power/state on
onrestart restart media
是不是一目了然了,啟動zygote service就是通過執行/system/bin/app_process進程來實現的。另外在進程中還創建了一個socket,這個socket用于和init進程進行通信,下文會介紹。
3守護進程
在init的main函數的最后,init會進入到一個無限循環中,作為一個守護進程。
for(;;) {//init進入無限循環
int nr, i, timeout = -1;
//檢查action_queue列表是否為空。如果不為空則移除并執行列表頭中的action
execute_one_command();
restart_processes();//這里會重啟所有flag標志為SVC_RESTARTING的service
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;
}
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;
}
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;
}
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (!action_queue_empty() || cur_action)
timeout = 0;
#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
//等待事件發生
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
for (i = 0; i < fd_count; i++) {
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())//處理keychord事件
handle_keychord();
else if (ufds[i].fd == get_signal_fd())//處理
handle_signal();//處理SIGCHLD信號
}
}
}
這里以Zygote的重啟為例,介紹一下守護進程的處理機制。當子進程退出時,init的這個信號處理函數會被調用:
static void sigchld_handler(int s)
{
write(signal_fd, &s, 1); //往signal_fd write數據
}
signal_fd,就是在init中通過socketpair創建的兩個socket中的一個,既然會往這個signal_fd中發送數據,那么另外一個socket就一定能接收到,這樣就會導致init從poll函數中返回
nr =poll(ufds, fd_count, timeout);
......
if(ufds[i].revents == POLLIN) {
......
else if (ufds[i].fd == get_signal_fd())//處理
handle_signal();//處理SIGCHLD信號
}
接下來就會調用handle_signal()函數來處理。init進程會找到死掉的那個service,然后先殺死該service創建的進程,清理socket信息,設置標示為SVC_RESTARTING,然后執行該service on restart中的COMMAND。接下來在init守護進程的下一次循環中啟動:
......
restart_processes();//這里會重啟所有flag標志為SVC_RESTARTING的service
......
4.屬性服務
在init進程的main()中,我們可以看見如下語句:
queue_builtin_action(property_service_init_action, "property_service_init");
這句話的意思是啟動action鏈表中的property服務:
static int property_service_init_action(int nargs, char **args)
{
/* read any property files on system or data and
* fire up the property service. This must happen
* after the ro.foo properties are set above so
* that /data/local.prop cannot interfere with them.
*/
start_property_service();
return 0;
}
start_property_service()的實現如下:
void start_property_service(void)
{
int fd;
load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
if(fd < 0) return;
fcntl(fd, F_SETFD, FD_CLOEXEC);
fcntl(fd, F_SETFL, O_NONBLOCK);
listen(fd, 8);
property_set_fd = fd;
}
關于這段代碼做一些說明:
在Android中定義了5個存儲屬性的文件,它們分別是:
#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
#define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop"
#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
#define PROP_PATH_FACTORY "/factory/factory.prop"
load_persistent_properties()用來加載persist開頭的屬性文件,這些屬性文件是需要保存到永久介質上的,這些屬性文件存儲在/data/property目錄下。
在start_property_service()的最后創建了一個名為"property_service"的socket,啟動監聽,并將socket的句柄保存在property_set_fd中。
處理設置屬性請求
接收屬性設置請求的地方在init進程中:
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
for (i = 0; i < fd_count; i++) {
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();
}
}
可以看到,在init接收到其他進程設置屬性的請求時,會調用handle_property_set_fd()函數進程處理,最后完成屬性設置。
參考文章: