Android啟動(dòng)過(guò)程詳解(2)——init進(jìn)程啟動(dòng)邏輯

init進(jìn)程是Android系統(tǒng)用戶空間中的第一個(gè)進(jìn)程碾褂,其進(jìn)程號(hào)也是1,足見(jiàn)其重要性枢析。所以它的責(zé)任也是重大的抵乓,概括地來(lái)說(shuō)init進(jìn)程主要做了以下幾件事:

  1. 作為守護(hù)進(jìn)程
  2. 解析和執(zhí)行init.rc文件
  3. 屬性服務(wù)
  4. 生成設(shè)備驅(qū)動(dòng)節(jié)點(diǎn)

接下來(lái)文章就著init進(jìn)程的源碼,來(lái)一個(gè)個(gè)分析init進(jìn)程的工作仇哆。首先來(lái)看看init進(jìn)程的源碼:

int main(int argc, char **argv)  
{  
    int fd_count = 0;  
    struct pollfd ufds[4];  
    char *tmpdev;  
    char* debuggable;  
    char tmp[32];  
    int property_set_fd_init = 0;  
    int signal_fd_init = 0;  
    int keychord_fd_init = 0;  
    bool is_charger = false;  
  
  
    //啟動(dòng)ueventd  
    if (!strcmp(basename(argv[0]), "ueventd"))  
        return ueventd_main(argc, argv);  
  
  
    //啟動(dòng)watchdogd  
    if (!strcmp(basename(argv[0]), "watchdogd"))  
        return watchdogd_main(argc, argv);  
  
  
    /* clear the umask */  
    umask(0);  
  
  
        /* 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. 
         */  
    //創(chuàng)建并掛在啟動(dòng)所需的文件目錄  
    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);  
  
  
        /* indicate that booting is in progress to background fw loaders, etc */  
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));//檢測(cè)/dev/.booting文件是否可讀寫(xiě)和創(chuàng)建  
  
  
        /* We must have some place other than / to create the 
         * device nodes for kmsg and null, otherwise we won't 
         * be able to remount / read-only later on. 
         * Now that tmpfs is mounted on /dev, we can actually 
         * talk to the outside world. 
         */  
    open_devnull_stdio();//重定向標(biāo)準(zhǔn)輸入/輸出/錯(cuò)誤輸出到/dev/_null_  
    klog_init();//log初始化  
    property_init();//屬性服務(wù)初始化  
  
  
    //從/proc/cpuinfo中讀取Hardware名沦辙,在后面的mix_hwrng_into_linux_rng_action函數(shù)中會(huì)將hardware的值設(shè)置給屬性ro.hardware  
    get_hardware_name(hardware, &revision);  
  
  
    //導(dǎo)入并設(shè)置內(nèi)核變量  
    process_kernel_cmdline();  
  
  
    //selinux相關(guān),暫不分析  
    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");  
  
  
    is_charger = !strcmp(bootmode, "charger");//關(guān)機(jī)充電相關(guān)税产,暫不做分析  
  
  
    INFO("property init\n");  
    if (!is_charger)  
        property_load_boot_defaults();  
  
  
    INFO("reading config file\n");  
    init_parse_config_file("/init.rc");//解析init.rc配置文件  
  
  
    /* 
     * 解析完init.rc后會(huì)得到一系列的action等怕轿,下面的代碼將執(zhí)行處于early-init階段的action。 
     * init將action按照?qǐng)?zhí)行時(shí)間段的不同分為early-init辟拷、init撞羽、early-boot、boot衫冻。 
     * 進(jìn)行這樣的劃分是由于有些動(dòng)作之間具有依賴(lài)關(guān)系诀紊,某些動(dòng)作只有在其他動(dòng)作完成后才能執(zhí)行,所以就有了先后的區(qū)別隅俘。 
     * 具體哪些動(dòng)作屬于哪個(gè)階段是在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);  
  
  
    /* skip mounting filesystems in charger mode */  
    if (!is_charger) {  
        action_for_each_trigger("early-fs", action_add_queue_tail);  
        action_for_each_trigger("fs", action_add_queue_tail);  
        action_for_each_trigger("post-fs", action_add_queue_tail);  
        action_for_each_trigger("post-fs-data", action_add_queue_tail);  
    }  
  
  
    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random 
     * wasn't ready immediately after wait_for_coldboot_done 
     */  
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  
  
    queue_builtin_action(property_service_init_action, "property_service_init");  
    queue_builtin_action(signal_init_action, "signal_init");  
    queue_builtin_action(check_startup_action, "check_startup");  
  
  
    if (is_charger) {  
        action_for_each_trigger("charger", action_add_queue_tail);  
    } else {  
        action_for_each_trigger("early-boot", action_add_queue_tail);  
        action_for_each_trigger("boot", action_add_queue_tail);  
    }  
  
  
        /* run all property triggers based on current state of the properties */  
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");  
  
  
  
  
#if BOOTCHART  
    queue_builtin_action(bootchart_init_action, "bootchart_init");  
#endif  
  
  
    for(;;) {//init進(jìn)入無(wú)限循環(huán)  
        int nr, i, timeout = -1;  
        //檢查action_queue列表是否為空邻奠。如果不為空則移除并執(zhí)行列表頭中的action  
        execute_one_command();  
        restart_processes();//重啟已經(jīng)死去的進(jìn)程  
  
  
        if (!property_set_fd_init && get_property_set_fd() > 0) {  
            ufds[fd_count].fd = get_property_set_fd();  
            ufds[fd_count].events = POLLIN;  
            ufds[fd_count].revents = 0;  
            fd_count++;  
            property_set_fd_init = 1;  
        }  
        if (!signal_fd_init && get_signal_fd() > 0) {  
            ufds[fd_count].fd = get_signal_fd();  
            ufds[fd_count].events = POLLIN;  
            ufds[fd_count].revents = 0;  
            fd_count++;  
            signal_fd_init = 1;  
        }  
        if (!keychord_fd_init && get_keychord_fd() > 0) {  
            ufds[fd_count].fd = get_keychord_fd();  
            ufds[fd_count].events = POLLIN;  
            ufds[fd_count].revents = 0;  
            fd_count++;  
            keychord_fd_init = 1;  
        }  
  
  
        if (process_needs_restart) {  
            timeout = (process_needs_restart - gettime()) * 1000;  
            if (timeout < 0)  
                timeout = 0;  
        }  
  
  
        if (!action_queue_empty() || cur_action)  
            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  
        //等待事件發(fā)生  
        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())//處理屬性服務(wù)事件  
                    handle_property_set_fd();  
                else if (ufds[i].fd == get_keychord_fd())//處理keychord事件  
                    handle_keychord();  
                else if (ufds[i].fd == get_signal_fd())//處理  
                    handle_signal();//處理SIGCHLD信號(hào)  
            }  
        }  
    }  
  
  
    return 0;  
}

1.文件掛載,生成驅(qū)動(dòng)節(jié)點(diǎn)

首先來(lái)看看main函數(shù)第一塊比較重要的部分为居,文件掛載:

 /* 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. 
         */  
    //創(chuàng)建并掛在啟動(dòng)所需的文件目錄  
    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);  


        /* indicate that booting is in progress to background fw loaders, etc */  
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));//檢測(cè)/dev/.booting文件是否可讀寫(xiě)和創(chuàng)建  


        /* We must have some place other than / to create the 
         * device nodes for kmsg and null, otherwise we won't 
         * be able to remount / read-only later on. 
         * Now that tmpfs is mounted on /dev, we can actually 
         * talk to the outside world. 
         */  
    open_devnull_stdio();//重定向標(biāo)準(zhǔn)輸入/輸出/錯(cuò)誤輸出到/dev/_null_  
    klog_init();//log初始化 

這部分代碼還是比較簡(jiǎn)單的碌宴,就是掛載一些文件的掛載。值得說(shuō)明的是:

1.tmpfs是一種虛擬內(nèi)存文件系統(tǒng)蒙畴,通常情況下該文件系統(tǒng)是常駐ram的贰镣,所以其讀取速度要高于內(nèi)存和磁盤(pán)。而/dev目錄則存放了訪問(wèn)硬件設(shè)備所需的驅(qū)動(dòng)程序文件膳凝,將tmpfs作用于驅(qū)動(dòng)目錄最主要的原因還是提高硬件設(shè)備的訪問(wèn)速度碑隆。

  1. devpts是一種虛擬終端文件系統(tǒng)。
  2. proc是一個(gè)基于內(nèi)存的文件系統(tǒng)蹬音,其主要的作用是完成內(nèi)核與應(yīng)用之間的數(shù)據(jù)交換上煤。
  3. sysfs是一種特殊的文件系統(tǒng),在Linux 2.6中引入著淆,用于將系統(tǒng)中的設(shè)備組織成層次結(jié)構(gòu)劫狠,并向用戶模式程序提供詳細(xì)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)信息拴疤,將proc、devpts嘉熊、devfs三種文件系統(tǒng)統(tǒng)一起來(lái)遥赚。
  4. /dev /proc /sysfs 等等目錄都是系統(tǒng)運(yùn)行是目錄,在Android系統(tǒng)編譯時(shí)是不存在的阐肤,它們都是由init進(jìn)程創(chuàng)建的凫佛。當(dāng)系統(tǒng)終止時(shí)他們也會(huì)消失。

最后構(gòu)造的目錄如下:


這里寫(xiě)圖片描述

另外孕惜,open_devnull_stdio()函數(shù)的作用是重定向標(biāo)準(zhǔn)輸入/輸出/錯(cuò)誤輸出到/dev/null愧薛。klog_init()用于初始化log,通過(guò)其實(shí)現(xiàn)可以看出log被打印到/dev/kmsg文件中衫画。主要在代碼中最后通過(guò)fcntl和unlink使得/dev/kmsg不可被訪問(wèn)毫炉,這就保證了只有l(wèi)og程序才可以訪問(wèn)。

2. 解析和執(zhí)行init.rc文件

init.rc文件定義了在系統(tǒng)啟動(dòng)時(shí)需要執(zhí)行的動(dòng)作也需要啟動(dòng)的服務(wù)削罩,這些服務(wù)對(duì)于整個(gè)Android系統(tǒng)的正常運(yùn)行都是至關(guān)重要的瞄勾,其中大家熟知的ZygoteService就是在init.rc文件中指定需要執(zhí)行的。所以首先來(lái)看看init.rc文件的語(yǔ)法弥激。

2.1 init.rc文件介紹

init.rc文件并不是普通的配置文件进陡,而是由一種被稱(chēng)為“Android初始化語(yǔ)言”(Android Init Language,這里簡(jiǎn)稱(chēng)為AIL)的腳本寫(xiě)成的文件微服。首先來(lái)看一下init.rc文件

#筆者注:引入其他rc文件趾疚,這部分文件也將被執(zhí)行
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.trace.rc

on early-init
    # Set init and its forked children's oom_adj
    write /proc/1/oom_adj -16

on init 
    #動(dòng)作列表
    mkdir /system
    mkdir /data 0771 system system
    mkdir /cache 0770 system cache
    mkdir /config 0500 root root

on boot
    ...

on charger
    class_start charger

on property:vold.decrypt=trigger_reset_main
    class_reset main

service evened /sbin/ueventd
service health /sbin/healthd

...
#zygote service
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \

 --start-system-server 

    socketzygote stream 666  #socket關(guān)鍵字表示OPTION

   onrestart write /sys/android_power/request_state wake #onrestart也是OPTION

   onrestart write /sys/power/state on

   onrestart restart media

以上就是init.rc文件的節(jié)選。下面先來(lái)介紹一下其對(duì)應(yīng)的語(yǔ)法以蕴。AIL由如下4部分組成糙麦。

  • 動(dòng)作(Actions)
  • 命令(Commands)
  • 服務(wù)(Services)
  • 選項(xiàng)(Options)

AIL在編寫(xiě)時(shí)需要分成多個(gè)部分(Section),而每一部分的開(kāi)頭需要指定Actions或Services丛肮。也就是說(shuō)赡磅,每一個(gè)Actions或Services確定一個(gè)Section。而所有的Commands和Options只能屬于最近定義的Section宝与。如果Commands和Options在第一個(gè)Section之前被定義焚廊,它們將被忽略。
Actions和Services的名稱(chēng)必須唯一伴鳖。如果有兩個(gè)或多個(gè)Action或Service擁有同樣的名稱(chēng)节值,那么init在執(zhí)行它們時(shí)將拋出錯(cuò)誤徙硅,并忽略這些Action和Service榜聂。
Actions的語(yǔ)法格式如下:

on trigger
command
command
command

所以action是以on開(kāi)頭的,init.rc中定義的early-init嗓蘑,init须肆,boot匿乃,early-boot都是action。comman的就是這個(gè)action需要執(zhí)行的命令豌汇。這些指令很好理解幢炸,都是Linux下的指令。以init為例拒贱,在這個(gè)階段創(chuàng)建了幾個(gè)文件宛徊,并且設(shè)置了對(duì)應(yīng)的權(quán)限和user。

on init 
    #動(dòng)作列表
    mkdir /system
    mkdir /data 0771 system system
    mkdir /cache 0770 system cache
    mkdir /config 0500 root root
這里寫(xiě)圖片描述

接下來(lái)再來(lái)看看service的結(jié)構(gòu)逻澳。

service [name] [pathname] [ argument ]*
[option]
[option]

比如servicemanager

service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm

Services的選項(xiàng)是服務(wù)的修飾符闸天,可以影響服務(wù)如何以及怎樣運(yùn)行。服務(wù)支持的選項(xiàng)如下:

  1. critical
    表明這是一個(gè)非常重要的服務(wù)斜做。如果該服務(wù)4分鐘內(nèi)退出大于4次苞氮,系統(tǒng)將會(huì)重啟并進(jìn)入 Recovery (恢復(fù))模式。
  2. disabled
    表明這個(gè)服務(wù)不會(huì)同與他同trigger (觸發(fā)器)下的服務(wù)自動(dòng)啟動(dòng)瓤逼。該服務(wù)必須被明確的按名啟動(dòng)笼吟。
  3. setenv [name] [value]
    在進(jìn)程啟動(dòng)時(shí)將環(huán)境變量[name]設(shè)置為[value]。
  4. socket [name][type] [perm] [ user [ group ] ]
    創(chuàng)建一個(gè)unix域的名為/dev/socket/name 的套接字霸旗,并傳遞它的文件描述符給已啟動(dòng)的進(jìn)程贷帮。type 必須是 "dgram","stream" 或"seqpacket"。用戶和組默認(rèn)是0定硝。
  5. user username
    在啟動(dòng)這個(gè)服務(wù)前改變?cè)摲?wù)的用戶名皿桑。此時(shí)默認(rèn)為 root。
  6. group groupname [groupname ]*
    在啟動(dòng)這個(gè)服務(wù)前改變?cè)摲?wù)的組名蔬啡。除了(必需的)第一個(gè)組名诲侮,附加的組名通常被用于設(shè)置進(jìn)程的補(bǔ)充組(通過(guò)setgroups函數(shù)),檔案默認(rèn)是root箱蟆。
  7. oneshot
    服務(wù)退出時(shí)不重啟沟绪。
  8. class name
    指定一個(gè)服務(wù)類(lèi)。所有同一類(lèi)的服務(wù)可以同時(shí)啟動(dòng)和停止空猜。如果不通過(guò)class選項(xiàng)指定一個(gè)類(lèi)绽慈,則默認(rèn)為"default"類(lèi)服務(wù)。
  9. onrestart
    當(dāng)服務(wù)重啟辈毯,執(zhí)行一個(gè)命令

2.2 init.rc文件解析

接下來(lái)正式進(jìn)入到init.rc文件的解析過(guò)程坝疼,在init程序中有這樣一行代碼:

init_parse_config_file("/init.rc");//解析init.rc配置文件

其對(duì)應(yīng)的實(shí)現(xiàn)是

int init_parse_config_file(const char *fn)  
{  
    char *data;  
    data = read_file(fn, 0);  
    if (!data) return -1;  
  
    parse_config(fn, data);  
    DUMP();  
    return 0;  
}

接下來(lái)再來(lái)看看parse_config

static void parse_config(const char *fn, char *s)  
{  
    struct parse_state state;  
    struct listnode import_list;//導(dǎo)入鏈表,用于保持在init.rc中通過(guò)import導(dǎo)入的其他rc文件  
    struct listnode *node;  
    char *args[INIT_PARSER_MAXARGS];  
    int nargs;  
  
  
    nargs = 0;  
    state.filename = fn;//初始化filename的值為init.rc文件  
    state.line = 0;//初始化行號(hào)為0  
    state.ptr = s;//初始化ptr指向s谆沃,即read_file讀入到內(nèi)存中的init.rc文件的首地址  
    state.nexttoken = 0;//初始化nexttoken的值為0  
    state.parse_line = parse_line_no_op;//初始化行解析函數(shù)  
  
  
    list_init(&import_list);  
    state.priv = &import_list;  
  
  
    for (;;) {  
        switch (next_token(&state)) {  
        case T_EOF://如果返回為T(mén)_EOF钝凶,表示init.rc已經(jīng)解析完成,則跳到parser_done解析import進(jìn)來(lái)的其他rc腳本  
            state.parse_line(&state, 0, 0);  
            goto parser_done;  
        case T_NEWLINE:  
            state.line++;//一行讀取完成后唁影,行號(hào)加1  
            if (nargs) {//如果剛才解析的一行為語(yǔ)法行(非注釋等)耕陷,則nargs的值不為0掂名,需要對(duì)這一行進(jìn)行語(yǔ)法解析  
                int kw = lookup_keyword(args[0]);//init.rc中每一個(gè)語(yǔ)法行均是以一個(gè)keyword開(kāi)頭的,因此args[0]即表示這一行的keyword  
                if (kw_is(kw, SECTION)) {  
                    state.parse_line(&state, 0, 0);  
                    parse_new_section(&state, kw, nargs, args);  
                } else {  
                    state.parse_line(&state, nargs, args);  
                }  
                nargs = 0;//復(fù)位  
            }  
            break;  
        case T_TEXT://將nexttoken解析的一個(gè)text保存到args字符串?dāng)?shù)組中哟沫,nargs的最大值為INIT_PARSER_MAXARGS(64)饺蔑,即init.rc中一行最多不能超過(guò)INIT_PARSER_MAXARGS個(gè)text(單詞)  
            if (nargs < INIT_PARSER_MAXARGS) {  
                args[nargs++] = state.text;  
            }  
            break;  
        }  
    }  
  
  
......
}

代碼的總體邏輯比較簡(jiǎn)單,解析文件中的關(guān)鍵字嗜诀、換行符等等解析各個(gè)屬性猾警,然后添加到對(duì)應(yīng)的section中。后續(xù)代碼邏輯也比較信息隆敢,讀者可以自行看源碼肿嘲,這里因?yàn)槠蚓筒辉倮^續(xù)介紹源碼了。
解析init.rc文件的最終效果就是解析文件中的各個(gè)action和service筑公,然后將其添加到action_list和service_list中雳窟。供后續(xù)執(zhí)行l(wèi)ist中的action和service。

而在解析完成后匣屡,有這樣一段代碼:

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);  
  
  
    /* skip mounting filesystems in charger mode */  
    if (!is_charger) {  
        action_for_each_trigger("early-fs", action_add_queue_tail);  
        action_for_each_trigger("fs", action_add_queue_tail);  
        action_for_each_trigger("post-fs", action_add_queue_tail);  
        action_for_each_trigger("post-fs-data", action_add_queue_tail);  
    }  
  
  
    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random 
     * wasn't ready immediately after wait_for_coldboot_done 
     */  
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  
  
    queue_builtin_action(property_service_init_action, "property_service_init");  
    queue_builtin_action(signal_init_action, "signal_init");  
    queue_builtin_action(check_startup_action, "check_startup");  
  
  
    if (is_charger) {  
        action_for_each_trigger("charger", action_add_queue_tail);  
    } else {  
        action_for_each_trigger("early-boot", action_add_queue_tail);  
        action_for_each_trigger("boot", action_add_queue_tail);  
    }  
  
  
        /* run all property triggers based on current state of the properties */  
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");  
  
  
  
  
#if BOOTCHART  
    queue_builtin_action(bootchart_init_action, "bootchart_init");  
#endif  

這部分代碼就比較好理解了封救,執(zhí)行已經(jīng)解析到的service和action。

2.3 Zygote service的啟動(dòng)

看了以上代碼可能大家會(huì)奇怪捣作,對(duì)應(yīng)執(zhí)行的都是action誉结,怎么沒(méi)有看到service的執(zhí)行,其實(shí)service的啟動(dòng)已經(jīng)包含在了action的執(zhí)行當(dāng)中了券躁。以Zygote為例惩坑,這個(gè)service的啟動(dòng)就是放在boot過(guò)程中的。接下來(lái)重點(diǎn)講一下也拜。
在init.rc下的action中有一個(gè)command叫做
class_start [classservice]
指令的意思就是啟動(dòng)屬于classservice這個(gè)類(lèi)別的所有service以舒。而在boot階段就有一個(gè)command是:
start_service default
所以在boot階段會(huì)啟動(dòng)所有的default的service÷或許你已經(jīng)猜到了蔓钟,沒(méi)錯(cuò),Zygote就屬于default類(lèi)型卵贱,因而在boot階段會(huì)被執(zhí)行滥沫。關(guān)于Zygote如何被設(shè)置為default,具體的工作在parse_service階段完成的键俱,讀者可以自行分析源碼兰绣,這里不做介紹。
相應(yīng)的啟動(dòng)服務(wù)的操作也比較簡(jiǎn)單,在init進(jìn)程中通過(guò)fork编振,創(chuàng)建一個(gè)新的進(jìn)程缀辩,用于運(yùn)行Zygote,相關(guān)代碼如下:

if(pid == 0) {
    //pid為零,我們?cè)谧舆M(jìn)程中
       struct socketinfo *si;
       struct svcenvinfo *ei;
       char tmp[32];
       int fd, sz;
//得到屬性存儲(chǔ)空間的信息并加到環(huán)境變量中雌澄,后面在屬性服務(wù)一節(jié)中會(huì)碰到使用它的地方。
       get_property_workspace(&fd, &sz);
       add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        //添加環(huán)境變量信息
       for (ei = svc->envvars; ei; ei = ei->next)
           add_environment(ei->name, ei->value);
        //根據(jù)socketinfo創(chuàng)建socket
       for (si = svc->sockets; si; si = si->next) {
           int s = create_socket(si->name,
                                  !strcmp(si->type,"dgram") ?
                                  SOCK_DGRAM :SOCK_STREAM,
                                  si->perm,si->uid, si->gid);
           if (s >= 0) {
               //在環(huán)境變量中添加socket信息杯瞻。
                publish_socket(si->name, s);
           }
        }
       ......//設(shè)置uid镐牺,gid等
     setpgid(0, getpid());
       if(!dynamic_args) {
        /*
執(zhí)行/system/bin/app_process惋戏,這樣就進(jìn)入到app_process的main函數(shù)中了列赎。
fork、execve這兩個(gè)函數(shù)都是Linux系統(tǒng)上常用的系統(tǒng)調(diào)用矗晃。
        */
            if (execve(svc->args[0], (char**)svc->args, (char**) ENV) < 0) {
              ......
           }
        }else {
          ......
    }

為什么要執(zhí)行/system/bin/app_process?再來(lái)看看Zygote在init.rc文件中的定義:

#zygote service
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \
 --start-system-server 
    socketzygote stream 666  #socket關(guān)鍵字表示OPTION
   onrestart write /sys/android_power/request_state wake #onrestart也是OPTION
   onrestart write /sys/power/state on
   onrestart restart media

是不是一目了然了旗唁,啟動(dòng)zygote service就是通過(guò)執(zhí)行/system/bin/app_process進(jìn)程來(lái)實(shí)現(xiàn)的畦浓。另外在進(jìn)程中還創(chuàng)建了一個(gè)socket,這個(gè)socket用于和init進(jìn)程進(jìn)行通信检疫,下文會(huì)介紹讶请。

3守護(hù)進(jìn)程

在init的main函數(shù)的最后,init會(huì)進(jìn)入到一個(gè)無(wú)限循環(huán)中屎媳,作為一個(gè)守護(hù)進(jìn)程夺溢。

for(;;) {//init進(jìn)入無(wú)限循環(huán)  
        int nr, i, timeout = -1;  
        //檢查action_queue列表是否為空。如果不為空則移除并執(zhí)行列表頭中的action  
        execute_one_command();  
        restart_processes();//這里會(huì)重啟所有flag標(biāo)志為SVC_RESTARTING的service  
  
        if (!property_set_fd_init && get_property_set_fd() > 0) {  
            ufds[fd_count].fd = get_property_set_fd();  
            ufds[fd_count].events = POLLIN;  
            ufds[fd_count].revents = 0;  
            fd_count++;  
            property_set_fd_init = 1;  
        }  
        if (!signal_fd_init && get_signal_fd() > 0) {  
            ufds[fd_count].fd = get_signal_fd();  
            ufds[fd_count].events = POLLIN;  
            ufds[fd_count].revents = 0;  
            fd_count++;  
            signal_fd_init = 1;  
        }  
        if (!keychord_fd_init && get_keychord_fd() > 0) {  
            ufds[fd_count].fd = get_keychord_fd();  
            ufds[fd_count].events = POLLIN;  
            ufds[fd_count].revents = 0;  
            fd_count++;  
            keychord_fd_init = 1;  
        }  
  
        if (process_needs_restart) {  
            timeout = (process_needs_restart - gettime()) * 1000;  
            if (timeout < 0)  
                timeout = 0;  
        }  
  
        if (!action_queue_empty() || cur_action)  
            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  
        //等待事件發(fā)生  
        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())//處理屬性服務(wù)事件  
                    handle_property_set_fd();  
                else if (ufds[i].fd == get_keychord_fd())//處理keychord事件  
                    handle_keychord();  
                else if (ufds[i].fd == get_signal_fd())//處理  
                    handle_signal();//處理SIGCHLD信號(hào)  
            }  
        }  
    }  

這里以Zygote的重啟為例烛谊,介紹一下守護(hù)進(jìn)程的處理機(jī)制风响。當(dāng)子進(jìn)程退出時(shí),init的這個(gè)信號(hào)處理函數(shù)會(huì)被調(diào)用:

static void sigchld_handler(int s)
{  
   write(signal_fd, &s, 1); //往signal_fd write數(shù)據(jù)
}

signal_fd丹禀,就是在init中通過(guò)socketpair創(chuàng)建的兩個(gè)socket中的一個(gè)状勤,既然會(huì)往這個(gè)signal_fd中發(fā)送數(shù)據(jù),那么另外一個(gè)socket就一定能接收到双泪,這樣就會(huì)導(dǎo)致init從poll函數(shù)中返回

nr =poll(ufds, fd_count, timeout);
 ......
 if(ufds[i].revents == POLLIN) {
     ......
     else if (ufds[i].fd == get_signal_fd())//處理  
          handle_signal();//處理SIGCHLD信號(hào)  
 }

接下來(lái)就會(huì)調(diào)用handle_signal()函數(shù)來(lái)處理持搜。init進(jìn)程會(huì)找到死掉的那個(gè)service,然后先殺死該service創(chuàng)建的進(jìn)程焙矛,清理socket信息朵诫,設(shè)置標(biāo)示為SVC_RESTARTING,然后執(zhí)行該service on restart中的COMMAND。接下來(lái)在init守護(hù)進(jìn)程的下一次循環(huán)中啟動(dòng):

......
restart_processes();//這里會(huì)重啟所有flag標(biāo)志為SVC_RESTARTING的service
......

4.屬性服務(wù)

在init進(jìn)程的main()中薄扁,我們可以看見(jiàn)如下語(yǔ)句:

queue_builtin_action(property_service_init_action, "property_service_init");

這句話的意思是啟動(dòng)action鏈表中的property服務(wù):

static int property_service_init_action(int nargs, char **args)  
{  
    /* read any property files on system or data and 
     * fire up the property service.  This must happen 
     * after the ro.foo properties are set above so 
     * that /data/local.prop cannot interfere with them. 
     */  
    start_property_service();  
    return 0;  
}

start_property_service()的實(shí)現(xiàn)如下:

void start_property_service(void)  
{  
    int fd;  
  
    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);  
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);  
    load_override_properties();  
    /* Read persistent properties after all default values have been loaded. */  
    load_persistent_properties();  
  
    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);  
    if(fd < 0) return;  
    fcntl(fd, F_SETFD, FD_CLOEXEC);  
    fcntl(fd, F_SETFL, O_NONBLOCK);  
  
    listen(fd, 8);  
    property_set_fd = fd;  
}

關(guān)于這段代碼做一些說(shuō)明:

在Android中定義了5個(gè)存儲(chǔ)屬性的文件剪返,它們分別是:

#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"  
#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"  
#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"  
#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"  
#define PROP_PATH_FACTORY        "/factory/factory.prop"

load_persistent_properties()用來(lái)加載persist開(kāi)頭的屬性文件,這些屬性文件是需要保存到永久介質(zhì)上的邓梅,這些屬性文件存儲(chǔ)在/data/property目錄下脱盲。
在start_property_service()的最后創(chuàng)建了一個(gè)名為"property_service"的socket,啟動(dòng)監(jiān)聽(tīng)日缨,并將socket的句柄保存在property_set_fd中钱反。
處理設(shè)置屬性請(qǐng)求

接收屬性設(shè)置請(qǐng)求的地方在init進(jìn)程中:

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

可以看到,在init接收到其他進(jìn)程設(shè)置屬性的請(qǐng)求時(shí),會(huì)調(diào)用handle_property_set_fd()函數(shù)進(jìn)程處理,最后完成屬性設(shè)置面哥。

參考文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哎壳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子尚卫,更是在濱河造成了極大的恐慌归榕,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吱涉,死亡現(xiàn)場(chǎng)離奇詭異刹泄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)怎爵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)特石,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鳖链,你說(shuō)我怎么就攤上這事姆蘸。” “怎么了芙委?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵乞旦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我题山,道長(zhǎng)兰粉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任顶瞳,我火速辦了婚禮玖姑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘慨菱。我一直安慰自己焰络,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布符喝。 她就那樣靜靜地躺著闪彼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪协饲。 梳的紋絲不亂的頭發(fā)上畏腕,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音茉稠,去河邊找鬼描馅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛而线,可吹牛的內(nèi)容都是我干的铭污。 我是一名探鬼主播恋日,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嘹狞!你這毒婦竟也來(lái)了岂膳?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤磅网,失蹤者是張志新(化名)和其女友劉穎谈截,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體知市,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年速蕊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嫂丙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡规哲,死狀恐怖跟啤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唉锌,我是刑警寧澤隅肥,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站袄简,受9級(jí)特大地震影響腥放,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绿语,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一秃症、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吕粹,春花似錦种柑、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至稳其,卻和暖如春驶赏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背既鞠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工母市, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人损趋。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓患久,卻偏偏與公主長(zhǎng)得像椅寺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蒋失,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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