Android 系統啓動分析(轉)

 

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

1、init可執行程序

init 可執行文件是系統運行的第一個用戶空間程序,它以守護進程的方式運行。因此這個程序的init.c文件包含main函數的入口,基本分析如下:
int main(int argc, char **argv)
{
   (省略若干。。。)
   
    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/n");
    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/n");
    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 = "/n"
        "/n"
        "/n"
        "/n"
        "/n"
        "/n"
        "/n"  // console is 40 cols x 30 lines
        "/n"
        "/n"
        "/n"
        "/n"
        "/n"
        "/n"
        "/n"
      /*"             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");
    else if (!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/n");
        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/n");
    } else if (bootchart_count > 0) {
        NOTICE("bootcharting started (period=%d ms)/n", bootchart_count*BOOTCHART_POLLING_MS);
    } else {
        NOTICE("bootcharting ignored/n");
    }
#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是一些基本的操作,例如:

    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 mount

service 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裏的內容即可.

 

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