android init進程分析 基本流程

來源:互聯網
上載者:User

android init進程分析 基本流程

(懶人最近想起我還有csdn好久沒打理了,這個android init躺在我的草稿箱中快5年了,稍微改改發出來吧)

android裝置上電,引導程式引導進入boot(通常是uboot),載入initramfs、kernel鏡像,啟動kernel後,進入使用者態程式。第一個使用者空間程式是init, PID固定是1.在android系統上,init的代碼位於/system/core/init下,準系統有:

管理裝置解析並處理啟動指令碼init.rc即時維護這個init.rc中的服務

init進程的系統初始化和服務流程,可以簡單的看下init.c中的main函數。這裡簡要分析一下這個函數的主要作用,細節不展開或後繼再展開。

簡要說下main函數的各項操作吧:

    if (!strcmp(basename(argv[0]), ueventd))        return ueventd_main(argc, argv);    if (!strcmp(basename(argv[0]), watchdogd))        return watchdogd_main(argc, argv);

以上這段,是main函數入口的第一句,這是判斷是跑那個程式。

/system/core/init 這裡面的一份代碼,編出的二進位可執行程式init,實際是分為三個程式啟動並執行,指示大家共用一份代碼而已。三份分別是:
/init: 實際init可執行程式
/sbin/ueventd:ueventd daemon程式,是init的軟連結
/sbin/watchdogd: watchdogd daemon程式,也是init的軟連結。

我們都知道,main函數的參數argv的第一個,argv[0]為自身運行目錄路徑和程式名,這裡就根據這個條件來判斷代碼走的路徑。為何這樣做?其實完全可以再另外寫一個ueventd或watchdogd的一套程式,定義main函數啊。其實這裡原因也是很簡單,他們共用了太多東西,直接寫到一起多簡單!就像busybox或toolbox整合那麼多命令工具一樣的道理。

    /* clear the umask */    umask(0);

清理umask,這個主要是檔案許可權的問題,設了umask,可以全域mask掉一些檔案許可權。

        /* 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);

Linux需要的dev,proc, sys等檔案系統的載入。

 

        /* indicate that booting is in progress to background fw loaders, etc */    close(open(/dev/.booting, O_WRONLY | O_CREAT, 0000));

 

 

 

 

 

 

建立一個/dev/.booting檔案。 /dev是記憶體檔案系統,不會儲存的,每次開機都要重新建立。這個是指示,目前在booting過程中,具體幹什麼用的,介紹ueventd的時候就清楚了。就是載入裝置的fimware用的。

    open_devnull_stdio();    klog_init();    property_init();

這三句,分別是將 stdin,stdout和stderr先初始化到/dev/__null__,這樣用printf或其他列印的,都輸出不了,也不會引起其它異常(這個階段,其實不能用,會出錯的)。

注意這個/dev/__null__是臨時起的名字,建立node後刪了,不影響系統真正的/dev/null,這裡只需要fd即可,有了fd後,檔案名稱就無用了,有了還會有幹擾。

klog_init,是將init進程中的log,列印到核心printk的log buffer中的方法。這對調試init很有協助,畢竟此時沒有shell,通用的log輸出方法,如printf等還不能工作,藉助底層已有的核心調試功能當然是最好的了。

property的初始化也是在這裡,property讀取可以在各個使用者進程中做,但設定的入口必須是到init進程中來。在4.4上,property這塊修改了好多,現在是通過字典樹的方式儲存,可以支援更多的property屬性。

 get_hardware_name(hardware, &revision);

從 /proc/cpuinfo中讀到hardware資訊,設定到ro.hardware屬性中,便於後面解析 init.${ro.hardware}.rc使用

    process_kernel_cmdline();

kernel的command參數,解析後也放到property中,供以後的子進程或其他服務等使用。

    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);

selinux的初始化和檢查,沒仔細研究selinux,則個檢查過程有時候還挺耗費時間的。

    INFO(property init);    if (!is_charger)        property_load_boot_defaults();

這個是載入並設定properties。這些是預置的property,通常是/system/build.prop和default.prop檔案中預置的那些property。

init_parse_config_file(/init.rc);

這是開始解析init.rc檔案了。細節和init.rc的格式、寫法不說了,網上到處都是。主要說一下常見問題:
1. 下載代碼的伺服器,umask有時候可能會有影響,init.rc檔案other和group使用者是不能有寫入權限的,編譯的PC的umask最好設定成0022。
2. 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);    ...

這是開始處理init.rc中parser好的各個命令了。

action_for_each_trigger是對此類trigger所在呃所有命令,都加入到actions的list中去。對實際代碼或項目上要全域的看一下,所有的*.rc的同一個trigger都一起處理的。

queue_builtin_action這是內建的actions,也是將actions動作加入到actions list中。

這裡需要注意的是,各個trigger的載入順序,先加入的先執行,後加入的後執行,要特別注意,尤其是要修改init.rc檔案的時候,不瞭解這個容易因為前後依賴關係造成問題。

 

都準備好了的話,就到了服務處理階段了,這是一個死迴圈,主要工作就是:
1. 將init.rc及內建的actions命令,一條一條執行
2. 負責對service的管理。
3. 對signal及進程退出的處理
4. 響應property設定的請求(設定都在init中統一設定,讀取進程可以自己讀共用記憶體)

    for(;;) {        int nr, i, timeout = -1;        execute_one_command();        restart_processes();...

有時候需要最佳化開機的話,可以測量一下execute_on_command中的命令執行時間,把較長的(比如大於50ms)的列印出來,再想辦法最佳化一下。

每次迴圈,執行一條命令,檢查是否有需要重啟的服務..

        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();            }        }

多路polling,當polling到東西,就執行相應的動作。

timeout時間到,執行下輪迴圈。init.rc中的command沒處理完的話,timeout是0,這樣之前的actions list可以一順下來都執行掉。
注意,init.rc中定義的服務,是在class_start這個command中做的。

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.