Android 根文件系統啓動分析

下邊是我從網上找的,一個關於Android根文件系統啓動的過程的分析,以我我的的經驗,我感受主要把後面講的 init.rc分析看明白就好了。sql

 

init進程是Android啓動後系統執行的第一個名稱爲init的可執行程序。這個程序以一個守護進程的方式運行,它提供瞭如下功能:socket

 

設備管理函數

解析啓動腳本ui

執行啓動腳本中的基本功能spa

執行啓動腳本中的各類功能命令行

 

1init可執行程序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.hardwarero.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);

 

 /*執行全部觸發標識爲initaction*/

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

 

 

 /* *進入主進程循環: * - 重置輪詢事件的接受狀態,revents0 * - 查詢action隊列並執行。 * - 重啓須要重啓的服務 * - 輪詢註冊的事件 * - 若是signal_recv_fdreventsPOLLIN,則獲得一個信號,獲取並處理 * -若是device_fdreventsPOLLIN,調用handle_device_fd * - 若是property_fdreventsPOLLIN,調用handle_property_set_fd * - 若是keychord_fdreventsPOLLIN,調用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分別是他們所對應的可執行程序。 socketusergrouponeshot就是配合服務使用的選項。

 

 Properties(屬性)是系統中使用的一些值,能夠進行設置和讀取

setprop ro.FOREGROUND_APP_MEM 1536 setprop ro.VISIBLE_APP_MEM 2048 start adbd

setprop 用於設置屬性,on property能夠用於判斷屬性,這裏的屬性在整個Android系統運行中都是一致的。

 

綜上若是想要修改啓動過程只須要修改init.c或者init.rc裏的內容便可,本人只作了修改啓動界面顯示的實驗.

參考資料

Android 系統原理及開發要點詳解》

相關文章
相關標籤/搜索