Android系統啓動-Init篇

基於Android 6.0的源碼剖析, 分析Android啓動過程進程號爲1的init進程的工作內容

/system/core/init/
  - init.cpp
  - init_parser.cpp
  - signal_handler.cpp

一、概述

init是Linux系統中用戶空間的第一個進程,進程號爲1。Kernel啓動後,在用戶空間,啓動init進程,並調用init中的main()方法執行init進程的職責。對於init進程的功能分爲4部分:

  • 分析和運行所有的init.rc文件;
  • 生成設備驅動節點; (通過rc文件創建)
  • 處理子進程的終止(signal方式);
  • 提供屬性服務。

接下來從main()方法說起。

1.1 main

[-> init.cpp]

int main(int argc, char** argv) {
    ...
    umask(0); //設置文件屬性0777
    
    klog_init();  //初始化kernel log,位於設備節點/dev/kmsg【見小節1.2】
    klog_set_level(KLOG_NOTICE_LEVEL); //設置輸出的log級別
    // 輸出init啓動階段的log
    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
    
    property_init(); //創建一塊共享的內存空間,用於屬性服務
    signal_handler_init();  //初始化子進程退出的信號處理過程【見小節2.1】

    property_load_boot_defaults(); //加載default.prop文件
    start_property_service();   //啓動屬性服務器(通過socket通信)【5.1】
    init_parse_config_file("/init.rc"); //解析init.rc文件

    //執行rc文件中觸發器爲 on early-init的語句
    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");
    // 屏幕上顯示Android靜態Logo 【見小節1.3】
    queue_builtin_action(console_init_action, "console_init");
    
    //執行rc文件中觸發器爲 on init的語句
    action_for_each_trigger("init", action_add_queue_tail);
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    
    char bootmode[PROP_VALUE_MAX];
    //當處於充電模式,則charger加入執行隊列;否則late-init加入隊列。
    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
       action_for_each_trigger("charger", action_add_queue_tail);
    } else {
       action_for_each_trigger("late-init", action_add_queue_tail);
    }
    //觸發器爲屬性是否設置
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
     
    while (true) {
        if (!waiting_for_exec) {
            execute_one_command();
            restart_processes(); //【見小節1.4】
        }
        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }
        if (!action_queue_empty() || cur_action) {
            timeout = 0;
        }

        epoll_event ev;
        //循環 等待事件發生
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }
    return 0;
}

1.2 log系統

此時android的log系統還沒有啓動,採用kernel的log系統,打開的設備節點/dev/kmsg, 那麼可通過cat /dev/kmsg來獲取內核log。

接下來,設置log的輸出級別爲KLOG_NOTICE_LEVEL(5),當log級別小於5時則會輸出到kernel log, 默認值爲3.

#define KLOG_ERROR_LEVEL   3
#define KLOG_WARNING_LEVEL 4
#define KLOG_NOTICE_LEVEL  5
#define KLOG_INFO_LEVEL    6
#define KLOG_DEBUG_LEVEL   7
#define KLOG_DEFAULT_LEVEL  3 //默認爲3

1.3 console_init_action

[-> init.cpp]

static int console_init_action(int nargs, char **args)
{
    char console[PROP_VALUE_MAX];
    if (property_get("ro.boot.console", console) > 0) {
        snprintf(console_name, sizeof(console_name), "/dev/%s", console);
    }

    int fd = open(console_name, O_RDWR | O_CLOEXEC);
    if (fd >= 0)
        have_console = 1;
    close(fd);

    fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
    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 ";
        write(fd, msg, strlen(msg));
        close(fd);
    }

    return 0;
}

這便是開機顯示的底部帶ANDROID字樣的畫面。

1.4 restart_processes

[-> init.cpp]

static void restart_processes()
{
    process_needs_restart = 0;
    service_for_each_flags(SVC_RESTARTING,
                           restart_service_if_needed);
}

檢查service_list中的所有服務,對於帶有SVC_RESTARTING標誌的服務,則都會調用其相應的restart_service_if_needed。

static void restart_service_if_needed(struct service *svc)
{
    time_t next_start_time = svc->time_started + 5;

    if (next_start_time <= gettime()) {
        svc->flags &= (~SVC_RESTARTING);
        service_start(svc, NULL); 
        return;
    }

    if ((next_start_time < process_needs_restart) ||
        (process_needs_restart == 0)) {
        process_needs_restart = next_start_time;
    }
}

之後再調用service_start來啓動服務。

二、信號處理

在init.cpp的main()方法中,通過signal_handler_init()來初始化信號處理過程。

主要工作:

  • 初始化signal句柄;
  • 循環處理子進程;
  • 註冊epoll句柄;
  • 處理子進程的終止;

2.1 signal_handler_init

[-> signal_handler.cpp]

void signal_handler_init() {
    int s[2];
    // 創建socket pair
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        exit(1);
    }
    signal_write_fd = s[0];
    signal_read_fd = s[1];
    //當捕獲信號SIGCHLD,則寫入signal_write_fd
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    //SA_NOCLDSTOP使init進程只有在其子進程終止時纔會受到SIGCHLD信號
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);
    reap_any_outstanding_children(); 【見小節2.2】
    register_epoll_handler(signal_read_fd, handle_signal);  //【見小節2.3】
}

每個進程在處理其他進程發送的signal信號時都需要先註冊,當進程的運行狀態改變或終止時會產生某種signal信號,init進程是所有用戶空間進程的父進程,當其子進程終止時產生SIGCHLD信號,init進程調用信號安裝函數sigaction(),傳遞參數給sigaction結構體,便完成信號處理的過程。

這裏有兩個重要的函數:SIGCHLD_handler和handle_signal,如下:

//寫入數據
static void SIGCHLD_handler(int) {
    //向signal_write_fd寫入1,直到成功爲止
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
        ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
    }
}

//讀取數據
static void handle_signal() {
    char buf[32];
    //讀取signal_read_fd數據,放入buf
    read(signal_read_fd, buf, sizeof(buf));
    reap_any_outstanding_children(); 【見流程3-1】
}

2.2 reap_any_outstanding_children

[-> signal_handler.cpp]

static void reap_any_outstanding_children() {
    while (wait_for_one_process()) { }
}

static bool wait_for_one_process() {
    int status;
    //等待任意子進程,如果子進程沒有退出則返回0,否則則返回該子進程pid。
    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
    if (pid == 0) {
        return false;
    } else if (pid == -1) {
        ERROR("waitpid failed: %s\n", strerror(errno));
        return false;
    }
    service* svc = service_find_by_pid(pid); //根據pid查找到相應的service
    std::string name;

    if (!svc) {
        return true;
    }

    //當flags爲RESTART,且不是ONESHOT時,先kill進程組內所有的子進程或子線程
    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
        kill(-pid, SIGKILL);
    }

    //移除當前服務svc中的所有創建過的socket
    for (socketinfo* si = svc->sockets; si; si = si->next) {
        char tmp[128];
        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
        unlink(tmp);
    }

    //當flags爲EXEC時,釋放相應的服務
    if (svc->flags & SVC_EXEC) {
        waiting_for_exec = false;
        list_remove(&svc->slist);
        free(svc->name);
        free(svc);
        return true;
    }
    svc->pid = 0;
    svc->flags &= (~SVC_RUNNING);

    //對於ONESHOT服務,使其進入disabled狀態
    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
        svc->flags |= SVC_DISABLED;
    }
    //禁用和重置的服務,都不再自動重啓
    if (svc->flags & (SVC_DISABLED | SVC_RESET))  {
        svc->NotifyStateChange("stopped"); //設置相應的service狀態爲stopped
        return true;
    }

    //服務在4分鐘內重啓次數超過4次,則重啓手機進入recovery模式
    time_t now = gettime();
    if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
                return true;
            }
        } else {
            svc->time_crashed = now;
            svc->nr_crashed = 1;
        }
    }
    svc->flags &= (~SVC_RESTART);
    svc->flags |= SVC_RESTARTING;

    //執行當前service中所有onrestart命令
    struct listnode* node;
    list_for_each(node, &svc->onrestart.commands) {
        command* cmd = node_to_item(node, struct command, clist);
        cmd->func(cmd->nargs, cmd->args);
    }
    //設置相應的service狀態爲restarting
    svc->NotifyStateChange("restarting");
    return true;
}

另外:通過getprop | grep init.svc 可查看所有的service運行狀態。狀態總共分爲:running, stopped, restarting

2.3 register_epoll_handler

[-> signal_handler.cpp]

void register_epoll_handler(int fd, void (*fn)()) {
    epoll_event ev;
    ev.events = EPOLLIN; //可讀
    ev.data.ptr = reinterpret_cast<void*>(fn);
    //將fd的可讀事件加入到epoll_fd的監聽隊列中
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
        ERROR("epoll_ctl failed: %s\n", strerror(errno));
    }
}

三、rc文件語法

rc文件語法是以行尾單位,以空格間隔的語法,以#開始代表註釋行。rc文件主要包含Action、Service、Command、Options,其中對於Action和Service的名稱都是唯一的,對於重複的命名視爲無效。

3.1 Action

Action: 通過trigger,即以 on開頭的語句,決定何時執行相應的service。

  • on early-init; 在初始化早期階段觸發;
  • on init; 在初始化階段觸發;
  • on late-init; 在初始化晚期階段觸發;
  • on boot/charger: 當系統啓動/充電時觸發,還包含其他情況,此處不一一列舉;
  • on property:<key>=<value>: 當屬性值滿足條件時觸發;

3.2 Service

服務Service,以 service開頭,由init進程啓動,一般運行於另外一個init的子進程,所以啓動service前需要判斷對應的可執行文件是否存在。init生成的子進程,定義在rc文件,其中每一個service,在啓動時會通過fork方式生成子進程。

例如: service servicemanager /system/bin/servicemanager代表的是服務名爲servicemanager,服務的路徑,也就是服務執行操作時運行/system/bin/servicemanager。

3.3 Command

下面列舉常用的命令

  • class_start <service_class_name>: 啓動屬於同一個class的所有服務;
  • start <service_name>: 啓動指定的服務,若已啓動則跳過;
  • stop <service_name>: 停止正在運行的服務
  • setprop <name> <value>:設置屬性值
  • mkdir <path>:創建指定目錄
  • symlink <target> <sym_link>: 創建連接到<target>的<sym_link>符號鏈接;
  • write <path> <string>: 向文件path中寫入字符串;
  • exec: fork並執行,會阻塞init進程直到程序完畢;
  • exprot <name> <name>:設定環境變量;
  • loglevel <level>:設置log級別

3.4 Options

Options是Services的可選項,與service配合使用

  • disabled: 不隨class自動啓動,只有根據service名才啓動;
  • oneshot: service退出後不再重啓;
  • user/group: 設置執行服務的用戶/用戶組,默認都是root;
  • class:設置所屬的類名,當所屬類啓動/退出時,服務也啓動/停止,默認爲default;
  • onrestart:當服務重啓時執行相應命令;
  • socket: 創建名爲/dev/socket/<name>的socket
  • critical: 在規定時間內該service不斷重啓,則系統會重啓並進入恢復模式

default: 意味着disabled=false,oneshot=false,critical=false。

四、啓動服務

4.1 啓動順序

on early-init
on init
on late-init //掛載文件系統,啓動核心服務
    trigger post-fs
    trigger load_system_props_action 
    trigger post-fs-data //掛載data
    trigger load_persist_props_action
    trigger firmware_mounts_complete
    trigger boot
    
on post-fs
    start logd
    mount rootfs rootfs / ro remount
    mount rootfs rootfs / shared rec 
    mount none /mnt/runtime/default /storage slave bind rec
    ...
     
on post-fs-data
    start logd
    start vold //啓動vold
    ...
    
on boot
    ...
    class_start core //啓動core class

由on early-init -> init -> late-init -> boot。

先啓動core class, 至於main class的啓動是由vold.decrypt的以下4個值的設置所決定的, 該過程位於system/vold/cryptfs.c文件。

on nonencrypted
    class_start main
    class_start late_start
    
on property:vold.decrypt=trigger_restart_min_framework
    class_start main

on property:vold.decrypt=trigger_restart_framework
    class_start main
    class_start late_start

on property:vold.decrypt=trigger_reset_main
    class_reset main
        
on property:vold.decrypt=trigger_shutdown_framework
    class_reset late_start
    class_reset main

4.2 服務啓動(Zygote)

在init.zygote.rc文件中,zygote服務定義如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

通過init_parser.cpp完成整個service解析工作,此處就不詳細展開講解析過程,該過程主要是創建一個名”zygote”的service結構體,一個socketinfo結構體(用於socket通信),以及一個包含4個onrestart的action結構體。

Zygote服務會隨着main class的啓動而啓動,退出後會由init重啓zygote,即使多次重啓也不會進入recovery模式。zygote所對應的可執行文件是/system/bin/app_process,通過調用pid =fork()創建子進程,通過execve(svc->args[0], (char**)svc->args, (char**) ENV),進入App_main.cpp的main()函數。故zygote是通過fork和execv共同創建的。

流程如下:

zygote_init

而關於Zygote重啓在前面的信號處理過程中講過,是處理SIGCHLD信號,init進程重啓zygote進程,更多關於Zygote內容見Zygote篇

4.3 服務重啓

init_oneshot

當init子進程退出時,會產生SIGCHLD信號,併發送給init進程,通過socket套接字傳遞數據,調用到wait_for_one_process()方法,根據是否是oneshot,來決定是重啓子進程,還是放棄啓動。

所有的Service裏面只有servicemanager ,zygote ,surfaceflinger這3個服務有onrestart關鍵字來觸發其他service啓動過程。

//zygote可觸發media、netd重啓
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

//servicemanager可觸發healthd、zygote、media、surfaceflinger、drm重啓
service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

//surfaceflinger可觸發zygote重啓
service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    onrestart restart zygote

由上可知:

  • zygote:觸發media、netd以及子進程(包括system_server進程)重啓;
  • system_server: 觸發zygote重啓;
  • surfaceflinger:觸發zygote重啓;
  • servicemanager: 觸發zygote、healthd、media、surfaceflinger、drm重啓

所以,surfaceflinger,servicemanager,zygote自身以及system_server進程被殺都會觸發Zygote重啓。

五、屬性服務

當某個進程A,通過property_set()修改屬性值後,init進程會檢查訪問權限,當權限滿足要求後,則更改相應的屬性值,屬性值一旦改變則會觸發相應的觸發器(即rc文件中的on開頭的語句),在Android Shared Memmory(共享內存區域)中有一個_system_property_area_區域,裏面記錄着所有的屬性值。對於進程A通過property_get()方法,獲取的也是該共享內存區域的屬性值。

5.1 初始化

[-> property_service.cpp]

void property_init() {
    //用於保證只初始化_system_property_area_區域一次
    if (property_area_initialized) {
        return;
    }
    property_area_initialized = true;
    if (__system_property_area_init()) {
        return;
    }
    pa_workspace.size = 0;
    pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
    if (pa_workspace.fd == -1) {
        ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
        return;
    }
}

在properyty_init函數中,先調用init_property_area函數,創建一塊用於存儲屬性的共享內存,而共享內存是可以跨進程的。

關於加載的prop文件

通過load_all_load_all_propsprops()方法,加載以下:

  1. /system/build.prop;
  2. /vendor/build.prop;
  3. /factory/factory.prop;
  4. /data/local.prop;
  5. /data/property路徑下的persist屬性

5.2 屬性類別

  1. 屬性名以ctl.開頭,則表示是控制消息,控制消息用來執行一些命令。例如:
    • setprop ctl.start bootanim 查看開機動畫;
    • setprop ctl.stop bootanim 關閉開機動畫;
    • setprop ctl.start pre-recovery 進入recovery模式。
  2. 屬性名以ro.開頭,則表示是隻讀的,不能設置,所以直接返回。
  3. 屬性名以persist.開頭,則需要把這些值寫到對應文件中去。

以上內容轉自:gityuan的Android系統啓動-Init篇

 -----------------------------------------------------------分割線-------------------------------------------------------------------------------

以下內容爲個人小結:

serviceManager、surfaceflinger、zygote本身都會導致zygote的重啓,而zygote重啓會導致system_server重啓,而system_server的重啓,system_server在重啓前肯定會先把屬於它管理的應用進程都殺死。service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server這個service聲明就表明zygote的參數是--start-system-server,所以會去重啓system_server

 

 

 

 

 

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