Android FrameWork - 開機(jī)啟動(dòng) Init 進(jìn)程

相關(guān)文章鏈接:

1. Android FrameWork - 學(xué)習(xí)啟動(dòng)篇
2. Android FrameWork - 開機(jī)啟動(dòng) Init 進(jìn)程
3. Android FrameWork - 開機(jī)啟動(dòng) Zygote 進(jìn)程

相關(guān)源碼文件:

/system/core/init/Init.cpp
/system/core/rootdir/init.rc
/system/core/init/init_parser.cpp
/system/core/init/builtins.cpp
/system/core/init/signal_handler.cpp

操作系統(tǒng)本身也是一個(gè)程序,只是這個(gè)程序是用來管理我們 App 應(yīng)用程序的闷煤。不知大家是否了解計(jì)算機(jī)的啟動(dòng)過程童芹?當(dāng)然不了解也沒關(guān)系這里并不影響。如果大家想要了解操作系統(tǒng)的基本原理鲤拿,可以去聽聽國內(nèi)外的一些公開課假褪,推薦清華大學(xué)的《計(jì)算機(jī)操作系統(tǒng)》公開課。Android 系統(tǒng)雖然也是基于 Linux 系統(tǒng)的近顷,但是由于 Android 屬于移動(dòng)設(shè)備生音,并沒有像 PC 那樣的 BIOS 程序。 取而代之的是 BootLoader (系統(tǒng)啟動(dòng)加載器)幕庐。 它類似于 BIOS久锥,在系統(tǒng)加載前,用以初始化硬件設(shè)備异剥,建立內(nèi)存空間的映像圖瑟由,為最終調(diào)用系統(tǒng)內(nèi)核準(zhǔn)備好環(huán)境。 在 Android 里沒有硬盤青伤,而是 ROM,它類似于硬盤存放操作系統(tǒng)丰歌,用戶程序等。 ROM 跟硬盤一樣也會(huì)劃分為不同的區(qū)域晓勇,用于放置不同的程序枢泰。當(dāng) Linux 內(nèi)核啟動(dòng)后會(huì)初始化各種軟硬件環(huán)境稼稿,加載驅(qū)動(dòng)程序,掛載根文件系統(tǒng)谋右,Linux 內(nèi)核加載的準(zhǔn)備完畢后就開始加載一些特定的程序(進(jìn)程)了坑雅。第一個(gè)加載的就是 init 進(jìn)程终蒂,我們就從這里開始分析,至于之前的過程大家感興趣可以自行研究下睁蕾,作為一個(gè) App 應(yīng)用開發(fā)者不了解也沒關(guān)系。

Init 進(jìn)程

我們應(yīng)該都知道不管是 Java 還是 C/C++ 去運(yùn)行某一個(gè)程序(進(jìn)程)都是 XXX.xxx 的 main 方法作為入口壹店,相信有很多大佬都跟我一樣射窒,App 開發(fā)做久了漸漸就忘記了還有個(gè) main 方法蝌麸。因此我們找到 /system/core/init/Init.cpp 的 main() 方法:

int main(int argc, char** argv) {
    ...
    // 初始化 signal handler
    signal_handler_init();
    // 解析 init.rc 腳本文件
    init_parse_config_file("/init.rc"); 
    // 將解析腳本中對應(yīng)的操作添加到 action_queue 隊(duì)列中
    action_for_each_trigger("early-init", action_add_queue_tail);
    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    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");
    
    ...
    // 這里是個(gè)死循環(huán)
    while (true) {
        if (!waiting_for_exec) {
            // 執(zhí)行命令
            execute_one_command();
            restart_processes();
        }
        ...
    }
    return 0;
}

main 方法里面有 148 行代碼(不包括子函數(shù)代碼)具體分為四個(gè)步驟:1.創(chuàng)建目錄,掛載分區(qū)弟疆,2.解析啟動(dòng)腳本,3.啟動(dòng)解析的服務(wù)柑司,4.守護(hù)解析的服務(wù)。init.rc 文件是 Android 系統(tǒng)的重要配置文件玻粪,位于 /system/core/rootdir/init.rc

// 導(dǎo)入其它的一些腳本
import /init.environ.rc
import /init.usb.rc
// 當(dāng)前硬件版本的腳本
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc

on early-init
...
on init
...
// 服務(wù) 服務(wù)名稱 執(zhí)行文件路徑 執(zhí)行參數(shù)
// 有幾個(gè)重要的服務(wù)
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
service servicemanager /system/bin/servicemanager
service surfaceflinger /system/bin/surfaceflinger
service media /system/bin/mediaserver
service installd /system/bin/installd

上面主要分為有 import 導(dǎo)入、on 命令和 service 服務(wù)呢铆,最主要的幾個(gè)服務(wù)有 zygote线定、servicemanager纱皆、surfaceflinger 、media 近迁、installd (后面還會(huì)分析到),接著往下看是如何解析的:

int init_parse_config_file(const char* path) {
    INFO("Parsing %s...\n", path);
    Timer t;
    std::string data;
    if (!read_file(path, &data)) {
        return -1;
    }

    data.push_back('\n'); // TODO: fix parse_config.
    // 解析
    parse_config(path, data);
    dump_parser_state();

    NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
    return 0;
}

static void parse_config(const char *fn, const std::string& data)
{
    struct listnode import_list;
    struct listnode *node;
    
    for (;;) {
        // next_token 內(nèi)部實(shí)現(xiàn)是一個(gè)字符一個(gè)字符去解析
        // 0:T_EOF 文件結(jié)尾
        // \n: T_NEWLINE
        switch (next_token(&state)) {
        case T_EOF:
            // 文件結(jié)尾,跳轉(zhuǎn)到 parser_done
            state.parse_line(&state, 0, 0);
            goto parser_done;
        // 如果是新的一行,就認(rèn)為是一個(gè)新的命令
        case T_NEWLINE:
            state.line++;
            if (nargs) {
                // 不同的命令選擇不同的結(jié)構(gòu)體
                int kw = lookup_keyword(args[0]);
                if (kw_is(kw, SECTION)) {
                    // 是一個(gè) Section
                    state.parse_line(&state, 0, 0);
                    // 解析三種 Section:parse_service蛆橡、parse_action、parse_import
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    // 普通命令
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }

parser_done:
    // 循環(huán)解析 import 
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         int ret;

         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}

接下來看下是如何執(zhí)行的藐握,這里我們主要了解服務(wù)是如何啟動(dòng)的:

void execute_one_command() {
    Timer t;

    char cmd_str[256] = "";
    char name_str[256] = "";

    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
        cur_action = action_remove_queue_head();
        cur_command = NULL;
        if (!cur_action) {
            return;
        }

        build_triggers_string(name_str, sizeof(name_str), cur_action);

        INFO("processing action %p (%s)\n", cur_action, name_str);
        cur_command = get_first_command(cur_action);
    } else {
        cur_command = get_next_command(cur_action, cur_command);
    }

    if (!cur_command) {
        return;
    }
    // 服務(wù)的 func 跳轉(zhuǎn)到 service_for_each_class
    int result = cur_command->func(cur_command->nargs, cur_command->args);
    ...
}

int do_class_start(int nargs, char **args)
{
   /* Starting a class does not start services
   * which are explicitly disabled.  They must
   * be started individually.
   */
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}

void service_for_each_class(const char *classname,void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {
        svc = node_to_item(node, struct service, slist);
        // 如果名字是一致的執(zhí)行 func 初家,這是一個(gè)回調(diào)函數(shù)也就是 service_start_if_not_disabled
        if (!strcmp(svc->classname, classname)) {
            func(svc);
        }
    }
}

static void service_start_if_not_disabled(struct service *svc)
{
    if (!(svc->flags & SVC_DISABLED)) {
        service_start(svc, NULL);
    } else {
        svc->flags |= SVC_DISABLED_START;
    }
}

void service_start(struct service *svc, const char *dynamic_args)
{
    ...
    // fork 創(chuàng)建了一個(gè)子進(jìn)程
    pid_t pid = fork();
    if (pid == 0) {
        // 代表子進(jìn)程他托,子進(jìn)程會(huì)繼承父進(jìn)程的所有資源,可簡單理解為讀時(shí)共享寫事復(fù)制
        ...
        // exc 函數(shù)族籽腕,這些都是 linux 基礎(chǔ)知識
        execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
    }
}

最后再來看下 init 進(jìn)程是如何守護(hù)子進(jìn)程的:

void signal_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    // 創(chuàng)建 socket pair 用于通信
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        ERROR("socketpair failed: %s\n", strerror(errno));
        exit(1);
    }
    // 當(dāng)捕獲信號SIGCHLD,則寫入signal_write_fd
    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    act.sa_flags = SA_NOCLDSTOP;
    // SA_NOCLDSTOP 使 init 進(jìn)程只有在其子進(jìn)程終止時(shí)才會(huì)受到 SIGCHLD 信號
    sigaction(SIGCHLD, &act, 0);
    // 進(jìn)入 waitpid 來處理子進(jìn)程是否退出的情況
    reap_any_outstanding_children();
    // 調(diào)用 epoll_ctl 方法來注冊 epoll 的回調(diào)函數(shù)
    register_epoll_handler(signal_read_fd, handle_signal);
}

//讀取數(shù)據(jù)
static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    // 讀取 signal_read_fd 中的數(shù)據(jù),并放入 buf简珠,這里讀出并沒有什么實(shí)際作用,只是用于阻塞等待
    read(signal_read_fd, buf, sizeof(buf));
    reap_any_outstanding_children();
}

//寫入數(shù)據(jù)
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 reap_any_outstanding_children() {
    while (wait_for_one_process()) { }
}

static bool wait_for_one_process() {
    int status;
    //等待任意子進(jìn)程,如果子進(jìn)程沒有退出則返回 0脱货,否則則返回該子進(jìn)程 pid臼疫。
    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
    if (pid == 0) {
        return false;
    } else if (pid == -1) {
        return false;
    }
    //根據(jù) pid 查找到相應(yīng)的 service
    service* svc = service_find_by_pid(pid);

    //當(dāng) flags 為 RESTART哈打,且不是 ONESHOT 時(shí)湾盗, kill 進(jìn)程組內(nèi)所有的子進(jìn)程或子線程
    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
        kill(-pid, SIGKILL);
    }

    //移除當(dāng)前服務(wù) svc 中的所有創(chuàng)建過的 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);
    }

    //當(dāng) flags 為 EXEC 時(shí)帐萎,釋放相應(yīng)的服務(wù)
    if (svc->flags & SVC_EXEC) {
        waiting_for_exec = false;
        list_remove(&svc->slist);
        free(svc->name);
        free(svc);
        return true;
    }
    ...

    // 對于 ONESHOT 服務(wù),使其進(jìn)入 disabled 狀態(tài)
    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
        svc->flags |= SVC_DISABLED;
    }
    // 禁用和重置的服務(wù),都不再自動(dòng)重啟
    if (svc->flags & (SVC_DISABLED | SVC_RESET))  {
        svc->NotifyStateChange("stopped"); //設(shè)置相應(yīng)的service狀態(tài)為stopped
        return true;
    }

    // 執(zhí)行當(dāng)前 service 中所有 onrestart命令,這個(gè)就是重啟了
    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);
    }
    // 設(shè)置相應(yīng)的 service 狀態(tài)為 restarting
    svc->NotifyStateChange("restarting");
    return true;
}

至此 init 進(jìn)程已全部分析完畢摩窃,有四個(gè)步驟:1. 創(chuàng)建目錄,掛載分區(qū)兽叮,2. 解析啟動(dòng)腳本,3. 啟動(dòng)解析的服務(wù)猾愿,4. 守護(hù)解析的服務(wù)鹦聪。最需要注意的是 init 創(chuàng)建了 zygote(創(chuàng)建 App 應(yīng)用的服務(wù))、servicemanager (client 與 service 通信管理的服務(wù))蒂秘、surfaceflinger(顯示渲染服務(wù)) 和 media(多媒體服務(wù)) 等 service 進(jìn)程。

視頻地址:https://pan.baidu.com/s/1j_wgzITcgABVbThvO0VBPA
視頻密碼:jj4b

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缓升,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌目代,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眠菇,死亡現(xiàn)場離奇詭異,居然都是意外死亡智政,警方通過查閱死者的電腦和手機(jī)一罩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門彰居,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掘剪,“玉大人,你說我怎么就攤上這事锭硼“λ祝” “怎么了得滤?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵喜鼓,是天一觀的道長优烧。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮脸哀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扭吁。我一直安慰自己撞蜂,他們只是感情好白筹,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谅摄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪系馆。 梳的紋絲不亂的頭發(fā)上送漠,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音由蘑,去河邊找鬼闽寡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尼酿,可吹牛的內(nèi)容都是我干的爷狈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼裳擎,長吁一口氣:“原來是場噩夢啊……” “哼涎永!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鹿响,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤羡微,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后惶我,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妈倔,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年绸贡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盯蝴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡听怕,死狀恐怖捧挺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尿瞭,我是刑警寧澤松忍,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站筷厘,受9級特大地震影響鸣峭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜酥艳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一摊溶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧充石,春花似錦莫换、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坷剧。三九已至,卻和暖如春喊暖,著一層夾襖步出監(jiān)牢的瞬間惫企,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工陵叽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狞尔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓巩掺,卻偏偏與公主長得像偏序,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子胖替,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容