Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android系統啟動-init篇

Android系統啟動-init篇

編輯:關於Android編程

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

/system/core/init/Init.h
/system/core/init/Init.cpp
/system/core/init/Init_parser.h
/system/core/init/Init_parser.cpp
/system/core/init/Signal_handler.h
/system/core/init/Signal_handler.cpp

一、概述

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

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

1.1 main()

下面展示main()方法的骨干邏輯:

int main(int argc, char** argv) {
    ...
    klog_init();  //初始化kernel log
    property_init(); //創建一塊共享的內存空間,用於屬性服務
    signal_handler_init();  //初始化子進程退出的信號處理過程

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

    //執行rc文件中觸發器為 on early-init的語句
    action_for_each_trigger("early-init", action_add_queue_tail);
    //執行rc文件中觸發器為 on init的語句
    action_for_each_trigger("init", action_add_queue_tail);
    //執行rc文件中觸發器為 on late-init的語句
    action_for_each_trigger("late-init", action_add_queue_tail);

    while (true) {
        if (!waiting_for_exec) {
            execute_one_command();
            restart_processes();
        }
        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;
}

二、信號處理

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

1. 初始化signal句柄

signal_handler.cpp

【1-1】signal_handler_init

void signal_handler_init() {
    //創建信號SIGCHLD的機制
    int s[2];
    // 調用一對已連接好的socket(socketpair是syscall命令)
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        ERROR("socketpair failed: %s\n", strerror(errno));
        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; 【見流程1-2】
    //SA_NOCLDSTOP使init進程只有在其子進程終止時才會受到SIGCHLD信號
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);
    reap_any_outstanding_children(); 【見流程2-1】
    //對於handle_signal 【見流程2-3】
    register_epoll_handler(signal_read_fd, handle_signal);  //【見流程4】
}

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

【1-2】SIGCHLD_handler

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

【1-3】handle_signal

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

2. 循環處理子進程

signal_handler.cpp

【2-1】reap_any_outstanding_children

static void reap_any_outstanding_children() {
    while (wait_for_one_process()) { 【見流程2-2】
    }
}

【2-2】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) {
        INFO("SVC_EXEC pid %d finished...\n", svc->pid);
        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

3. 注冊epoll句柄

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

4. 處理子進程的終止

init_oneshot

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

三、rc文件語法

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

1. 動作Action

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

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

2. 服務Service

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

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

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級別

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。

所有的Service裡面只有servicemanager ,zygote ,surfaceflinger這3個service有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

在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篇。

五、屬性服務

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

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屬性

對於屬性

  1. 屬性名以ctl.開頭,則表示是控制消息,控制消息用來執行一些命令。例如:
    • setprop ctl.start bootanim 查看開機動畫;
    • setprop ctl.stop bootanim 關閉開機動畫;
    • setprop ctl.start pre-recovery 進入recovery模式。
  2. 屬性名以ro.`開頭,則表示是只讀的,不能設置,所以直接返回。
  3. 屬性名以persist.`開頭,則需要把這些值寫到對應文件中去。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved