下邊是我從網上找的,一個關於Android根文件系統啓動的過程的分析,以我我的的經驗,我感受主要把後面講的 init.rc分析看明白就好了。sql
init進程是Android啓動後系統執行的第一個名稱爲init的可執行程序。這個程序以一個守護進程的方式運行,它提供瞭如下功能:socket
設備管理函數
解析啓動腳本ui
執行啓動腳本中的基本功能spa
執行啓動腳本中的各類功能命令行
1、init可執行程序debug
init可執行文件是系統運行的第一個用戶空間程序,它以守護進程的方式運行。所以這個程序的init.c文件包含main函數的入口,基本分析以下:rest
int main(int argc, char **argv){server
(省略若干。。。)sqlite
umask(0); /*對umask進行清零。*/
mkdir("/dev", 0755);/*爲rootfs創建必要的文件夾,並掛載適當的分區。 */ 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); /*建立/dev/null和/dev/kmsg節點*/ open_devnull_stdio(); log_init(); /*解析/init.rc,將全部服務和操做信息加入鏈表。*/ INFO("reading config file ");
parse_config_file("/init.rc"); /*獲取內核命令行參數*/
qemu_init();
import_kernel_cmdline(0); /*先從上一步得到的全局變量中獲取信息硬件信息和版本號,若是沒有則從/proc/cpuinfo中提取, *並保存到全局變量。根據硬件信息選擇一個/init.(硬件).rc,並解析,將服務和操做信息加入鏈表。 */
get_hardware_name();
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
parse_config_file(tmp); /*執行鏈表中帶有「early-init」觸發的的命令。*/ action_for_each_trigger("early-init", action_add_queue_tail);
drain_action_queue(); /*遍歷/sys文件夾,是內核產生設備添加事件(爲了自動產生設備節點)。 *初始化屬性系統,並導入初始化屬性文件。用於在系統運行過程當中動態建立設備節點、刪除設備節點等操做 */ INFO("device init ");
device_fd = device_init();
property_init(); // 從屬性系統中獲得ro.debuggable,若爲1,則初始化keychord監聽。
debuggable = property_get("ro.debuggable");
if (debuggable && !strcmp(debuggable, "1")) {
keychord_fd = open_keychord();
}
/*打開console,若是cmdline中沒有指定的console則打開默認的/dev/console*/
if (console[0]) {
snprintf(tmp, sizeof(tmp), "/dev/%s", console);
console_name = strdup(tmp);
}
fd = open(console_name, O_RDWR);
if (fd >= 0) have_console = 1;
close(fd);
/*讀取/initlogo.rle(一張位圖),若是成功則在/dev/graphics/fb0顯示Logo,若是失敗則將/dev/tty0 *設爲TEXT模式並打開/dev/tty0,輸出文本ANDROID(本人修改成Zhao Rui Jia作爲啓動項目的修改)。 */
if( load_565rle_image(INIT_IMAGE_FILE) ) {
fd = open("/dev/tty0", O_WRONLY);
if (fd >= 0)
{
const char *msg; msg = " " " " " " " " " " " " " " // console is 40 cols x 30 lines " " " " " " " " " " " " " " /*" A N D R O I D ";*/ " z h a o R u i J i a";
write(fd, msg, strlen(msg));
close(fd);
}
}
/* 判斷cmdline 中的??,並設置屬性系統中的參數: * 1、 若是 bootmode爲 * - factory,設置ro.factorytest值爲1 * - factory2,設置ro.factorytest值爲2 * - 其餘的韻ro.factorytest值?0 * 2、若是有serialno參數,則設置ro.serialno,不然爲"" * 3、若是有bootmod參數,則設置ro.bootmod,不然爲"unknown" * 4、若是有baseband參數,則設置ro.baseband,不然爲"unknown" * 5、若是有carrier參數,則設置ro.carrier,不然爲"unknown" * 6、若是有bootloader參數,則設置ro.bootloader,不然爲"unknown" * 7、經過全局變量(前面從/proc/cpuinfo中提取的)設置ro.hardware和ro.version。 */
if(qemu[0])
import_kernel_cmdline(1);
if(!strcmp(bootmode,"factory"))
property_set("ro.factorytest","1");
elseif(!strcmp(bootmode,"factory2"))
property_set("ro.factorytest","2");
else property_set("ro.factorytest", "0");
property_set("ro.serialno",serialno[0] ? serialno : "");
property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
property_set("ro.baseband", baseband[0] ? baseband : "unknown");
property_set("ro.carrier", carrier[0] ? carrier : "unknown");
property_set("ro.bootloader",bootloader[0] ? bootloader : "unknown");
property_set("ro.hardware", hardware);
snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
property_set("ro.revision", tmp);
/*執行全部觸發標識爲init的action。*/
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
property_set_fd = start_property_service();
/* 爲sigchld handler建立信號機制*/
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);
}
/* 確認全部初始化工做完成 * device_fd(device init 完成) * property_set_fd(property server start 完成) * signal_recv_fd (信號機制創建) */
if ((device_fd < 0) || (property_set_fd < 0) || (signal_recv_fd < 0)) {
ERROR("init startup failure ");
Return 1;
}
/* execute all the boot actions to get us started */
action_for_each_trigger("early-boot",action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail); drain_action_queue();
/* run all property triggers based on current state of the properties */ queue_all_property_triggers();
drain_action_queue();
/* enable property triggers */
property_triggers_enabled = 1;
/* * 註冊輪詢事件: * - device_fd * - property_set_fd * -signal_recv_fd * -若是有keychord,則註冊keychord_fd */
ufds[0].fd = device_fd;
ufds[0].events = POLLIN;
ufds[1].fd = property_set_fd;
ufds[1].events = POLLIN;
ufds[2].fd = signal_recv_fd;
ufds[2].events = POLLIN;
fd_count = 3;
if (keychord_fd >0) {
ufds[3].fd = keychord_fd;
ufds[3].events = POLLIN;
fd_count++;
} else {
ufds[3].events = 0;
ufds[3].revents = 0;
}
/*若是支持BOOTCHART,則初始化BOOTCHART*/
#if BOOTCHART
bootchart_count = bootchart_init();
if (bootchart_count < 0) {
ERROR("bootcharting init failure ");
} else if (bootchart_count > 0) {
NOTICE("bootcharting started (period=%d ms) ", bootchart_count*BOOTCHART_POLLING_MS);
} else {
NOTICE("bootcharting ignored ");
}#endif
/* *進入主進程循環: * - 重置輪詢事件的接受狀態,revents爲0 * - 查詢action隊列並執行。 * - 重啓須要重啓的服務 * - 輪詢註冊的事件 * - 若是signal_recv_fd的revents爲POLLIN,則獲得一個信號,獲取並處理 * -若是device_fd的revents爲POLLIN,調用handle_device_fd * - 若是property_fd的revents爲POLLIN,調用handle_property_set_fd * - 若是keychord_fd的revents爲POLLIN,調用handle_keychord */
for(;;) {
int nr, i, timeout = -1;
for (i = 0; i < fd_count; i++)
ufds[i].revents = 0;
drain_action_queue();
restart_processes();
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0) 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;
if (ufds[2].revents == POLLIN) {
/* we got a SIGCHLD - reap and restart as needed */
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;
}
2、啓動腳本init.rc
在Android中使用啓動腳本init.rc,能夠在系統的初始化過程當中進行一些簡單的初始化操做。這個腳本被直接安裝到目標系統的根文件系統中,被init可執行程序解析。 init.rc是在init啓動後被執行的啓動腳本,其他發主要包含了如下內容:
Commands:命令
Actions:動做
Triggers:觸發條件
Services:服務
Options:選項
Propertise:屬性
Commands是一些基本的操做,例如:
mkdir /sdcard 0000 system system
mkdir /system
mkdir /data 0771 system system
mkdir /cache 0770 system cache
mkdir /config 0500 root root
mkdir /sqlite_stmt_journals 01777 root root
mount tmpfs tmpfs /sqlite_stmt_journals size=4m
這些命令在init可執行程序中被解析,而後調用相關的函數來實現。
Actions(動做)表示一系列的命令,一般在Triggers(觸發條件)中調用,動做和觸發條件,
例如:
on init export PATH /sbin:/system/sbin:/system/bin:/system/xbin
init表示一個觸發條件,這個觸發事件發生後,進行設置環境變量和創建目錄的操做稱爲一個「動做」 Services(服務)一般表示啓動一個可執行程序,Options(選項)是服務的附加內容,用於配合服務使用。
service vold /system/bin/vold socket vold stream 0660 root mountservice
bootsound /system/bin/playmp3 user media group audio oneshot
vold 和bootsound分別是兩個服務的名稱,/system/bin/vold和/system/bin/playmp3分別是他們所對應的可執行程序。 socket、user、group、oneshot就是配合服務使用的選項。
Properties(屬性)是系統中使用的一些值,能夠進行設置和讀取
。
setprop ro.FOREGROUND_APP_MEM 1536 setprop ro.VISIBLE_APP_MEM 2048 start adbd
setprop 用於設置屬性,on property能夠用於判斷屬性,這裏的屬性在整個Android系統運行中都是一致的。
綜上若是想要修改啓動過程只須要修改init.c或者init.rc裏的內容便可,本人只作了修改啓動界面顯示的實驗.
參考資料
《Android 系統原理及開發要點詳解》