Android啟動流程簡析(一)

最近一時興起,想對Android的啟動流程進行一次分析咳榜,經過一番整理踱阿,從以下幾個方面進行總結榔组,代碼部分只討論思路珊擂,不論細節(jié)瘤袖。

  1. Android架構介紹
  2. Android啟動概述
  3. BootLoader介紹
  4. Kernel初始化介紹
  5. Init初始化介紹
  6. Zygote啟動介紹
  7. SystemServer啟動介紹
  8. Launcher啟動介紹
  9. Log抓取與分析方法

由于發(fā)表文章的時候提示內容過長無法發(fā)布吼驶,于是把文章拆成了三部分發(fā)布:

  1. Android啟動流程簡析(一)
  2. Android啟動流程簡析(二)
  3. Android啟動流程簡析(三)

1. Android架構介紹

Android的架構可以從架構圖得知翼抠,主要分四層:

Android經典的四層架構圖
Android架構圖

每一層的作用不做介紹董虱,這里主要講涉及的鏡像有boot.img扼鞋、system.img、vendor.img空扎、recovery.img藏鹊、userdata.img、cache.img转锈,與平臺相關的鏡像有l(wèi)k.bin(MTK)盘寡、preloader.img(MTK)、logo.bin(MTK)撮慨、emmc_appsboot.mbn(QCOM)竿痰、splash.img(QCOM)等,通常來說砌溺,修改kernel層通常編譯boot.img即可影涉,修改Framework層或Native層主要是編譯system.img,在Android O之后修改某些模塊還需要編譯vendor.img规伐,主要是受Android O Treble的影響蟹倾,具體問題需要具體分析。

2. Android啟動概述

概述:Loader > Kernel > Native > Framework > Application

細分:BootRom > Bootloader > Kernel > Init > Zygote > SystemServer > Launcher

  • Loader層主要包括Boot Rom和Boot Loader
  • Kernel層主要是Android內核層
  • Native層主要是包括init進程以及其fork出來的用戶空間的守護進程猖闪、HAL層鲜棠、開機動畫等
  • Framework層主要是AMS和PMS等Service的初始化
  • Application層主要指SystemUI、Launcher的啟動

3. BootLoader介紹

Bootloader 就是在操作系統(tǒng)內核運行之前運行的一段小程序培慌。通過這段小程序豁陆,我們可以初始化硬件設備、建立內存空間的映射圖吵护,從而將系統(tǒng)的軟硬件環(huán)境帶到一個合適的狀態(tài)盒音,以便為最終調用操作系統(tǒng)內核準備好正確的環(huán)境表鳍。

調用流程:
crt0.S > kmain > arch_init > target_init > apps_init > aboot_init

3.1 crt0.S

  • 高通平臺:alps/bootable/bootloader/lk/arch/{paltform}/crt0.S
  • MTK平臺:alps/vendor/mediatek/proprietary/bootable/bootloader/lk/arch/{paltform}/crt0.S

platform主要有arm、arm64祥诽、x86譬圣、x86-64等,crt0.S代碼大體如下原押,在_start中先主要完成CPU初始化胁镐,禁用mmu偎血,禁用cache诸衔,初始化異常向量表等操作,最后將直接跳轉到函數kmain中

.section ".text.boot"
.globl _start
_start:
    b   reset
    b   arm_undefined
    b   arm_syscall
    b   arm_prefetch_abort
    b   arm_data_abort
    b   arm_reserved
    b   arm_irq
    b   arm_fiq

/*pre-loader to uboot argument Location*/
.global BOOT_ARGUMENT_LOCATION
BOOT_ARGUMENT_LOCATION:
        .word 0x00000000

    ...

#if (!ENABLE_NANDWRITE)
#if WITH_CPU_WARM_BOOT
    ldr     r0, warm_boot_tag
    cmp     r0, #1

    /* if set, warm boot */
    ldreq   pc, =BASE_ADDR

    mov     r0, #1
    str r0, warm_boot_tag
#endif
#endif

    ...

#if defined(ARM_CPU_CORTEX_A8) || defined(ARM_CPU_CORTEX_A9)
    DSB
    ISB
#endif

    bl      kmain
    b       .

3.2 kmain

  • 高通平臺:alps/bootable/bootloader/lk/kernel/main.c
  • MTK平臺:alps/vendor/mediatek/proprietary/bootable/bootloader/lk/kernel/main.c
/* called from crt0.S */
void kmain(void) __NO_RETURN __EXTERNALLY_VISIBLE;
void kmain(void)
{
#if !defined(MACH_FPGA) && !defined(SB_LK_BRINGUP)
    boot_time = get_timer(0);
#endif

    // get us into some sort of thread context
    thread_init_early();

    // early arch stuff
    arch_early_init();

    // do any super early platform initialization
    platform_early_init();

#if defined(MACH_FPGA) || defined(SB_LK_BRINGUP)
    boot_time = get_timer(0);
#endif

    // do any super early target initialization
    target_early_init();

    dprintf(INFO, "welcome to lk\n\n");

    // deal with any static constructors
    dprintf(SPEW, "calling constructors\n");
    call_constructors();

    // bring up the kernel heap
    dprintf(SPEW, "initializing heap\n");
    heap_init();

    // initialize the threading system
    dprintf(SPEW, "initializing threads\n");
    thread_init();

    // initialize the dpc system
    dprintf(SPEW, "initializing dpc\n");
    dpc_init();

    // initialize kernel timers
    dprintf(SPEW, "initializing timers\n");
    timer_init();

#ifdef  MTK_LK_IRRX_SUPPORT
    mtk_ir_init(0);
#endif

#if (!ENABLE_NANDWRITE)
    // create a thread to complete system initialization
    dprintf(SPEW, "creating bootstrap completion thread\n");

    thread_t *thread_bs2 = thread_create("bootstrap2", &bootstrap2, NULL,
    DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
    if (thread_bs2)
        thread_resume(thread_bs2);
    else {
        dprintf(CRITICAL, "Error: Cannot create bootstrap2 thread!\n");
        assert(0);
    }

    thread_t *thread_io = thread_create("iothread", &iothread, NULL,
    IO_THREAD_PRIORITY, DEFAULT_STACK_SIZE);
    if (thread_io)
        thread_resume(thread_io);
    else {
        dprintf(CRITICAL, "Error: Cannot create I/O thread!\n");
       assert(0);
    }

    // enable interrupts
    exit_critical_section();

    // become the idle thread
    thread_become_idle();
#else
    bootstrap_nandwrite();
#endif
}

kmain主要流程:

  1. 調用thread_init_early初始化線程系統(tǒng)
  2. 調用arch_early_init中判斷如果存在mmu就初始化颇玷,設置異常向量基地址笨农,使能中斷相關寄存器
  3. 在platform_early_init中完成初始化硬件時鐘、手機的主板等操作帖渠,這個函數每種cpu的實現都不一樣谒亦,定義在bootable\bootloader\lk\platform{cpu型號}\platform.c下
  4. target_early_init中完成初始化uart端口的操作,這個函數的實現在bootable\bootloader\lk\target{cpu型號}\init.c
  5. 調用函數heap_init完成內核堆棧的初始化空郊,用與kmalloc等函數的內存分配
  6. 在thread_init函數中初始化定時器
  7. 調用timer_init初始化內核定時器
  8. 如果沒有定義ENABLE_NANDWRITE份招,就創(chuàng)建出一個名為bootstrap2的線程,然后運行這個線程狞甚。退出臨界區(qū)锁摔,開中斷;如果定義了ENABLE_NANDWRITE哼审,在timer_init之后將執(zhí)行bootstrap_nandwrite

3.3 bootstrap2

static int bootstrap2(void *arg)
{
    dprintf(SPEW, "top of bootstrap2()\n");

    print_stack_of_current_thread();

    arch_init();

// XXX put this somewhere else
#if WITH_LIB_BIO
    bio_init();
#endif
#if WITH_LIB_FS
    fs_init();
#endif

    // initialize the rest of the platform
    dprintf(SPEW, "initializing platform\n");
    platform_init();

    // initialize the target
    dprintf(SPEW, "initializing target\n");
    target_init();

    dprintf(SPEW, "calling apps_init()\n");
    apps_init();

    return 0;
}

kmain bootstrap2階段:

  1. arch_init主要是打印一些信息
  2. target_init主要完成的操作有
    • 從共享內存中讀寫xbl提供的pmic信息
    • 初始化spmi總線谐腰,用于cpu和pmic通信
    • 初始化ap與rpm通信通道
    • 初始化按鍵
    • 判斷內核是否簽名,當使用的是簽名的內核時涩盾,需要初始化加密解密引擎
    • 判斷是從usf還是emmc啟動
    • 獲取分區(qū)表信息
    • 判斷電池電壓是否過低十气,過低則進入預充電
    • 和tz通信
    • 初始化emmc或ufs中的rpmb用戶加解密認證分區(qū)
    • 運行keymaster
  3. apps_init主要完成一些應用功能的初始化,并調用aboot_init

3.4 aboot_init

aboot_init在aboot.c中春霍,主要完成以下操作:

  1. 根據target_is_emmc_boot()判斷是否是從emmc存儲設備上啟動砸西,然后分別獲取對應存儲設備的頁大小和頁掩碼
  2. 取得設備的device_info信息,保存到device變量中
  3. 初始化lcd驅動址儒,顯示手機開機后的第一副圖片
  4. 獲取emmc或者flash芯片的產品序列號芹枷,最后在啟動kernel時通過cmdline中的androidboot.serialno參數傳給內核
  5. 檢查按鍵判斷是進入recovery還是fastboot
  6. 檢查重啟模式
  7. 跳轉到kernel

4. Kernel初始化介紹

Kernel初始化可以分成三部分:zImage解壓縮、kernel的匯編啟動階段离福、Kernel的C啟動階段

內核啟動引導地址由bootp.lds決定杖狼,內核啟動的執(zhí)行的第一條的代碼在head.S文件中,主要功能是實現壓縮內核的解壓和跳轉到內核vmlinux內核的入口

4.1 head.S

/*
 * Non-board-specific low-level startup code
 *
 * Copyright (C) 2004-2006 Atmel Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/linkage.h>

#include <asm/page.h>

    .section .init.text,"ax"
    .global kernel_entry
kernel_entry:
    /* Start the show */
    lddpc   pc, kernel_start_addr

    .align  2
kernel_start_addr:
    .long   start_kernel

kernel的C啟動階段可以理解為真正的啟動階段妖爷,從head.S看到蝶涩,最終調用的是kernel/init/main.c的start_kernel()函數

4.2 start_kernel

asmlinkage __visible void __init start_kernel(void)
{
    char *command_line;
    char *after_dashes;

    /*
     * Need to run as early as possible, to initialize the lockdep hash:
     */
    lockdep_init();
    set_task_stack_end_magic(&init_task);
    smp_setup_processor_id();
    debug_objects_early_init();

    /*
     * Set up the the initial canary ASAP:
     */
    boot_init_stack_canary();

    cgroup_init_early();

    local_irq_disable();
    early_boot_irqs_disabled = true;

    /*
     * Interrupts are still disabled. Do necessary setups, then
     * enable them
     */
    boot_cpu_init();
    page_address_init();
    pr_notice("%s", linux_banner);
    setup_arch(&command_line);
    mm_init_cpumask(&init_mm);
    setup_command_line(command_line);
    setup_nr_cpu_ids();
    setup_per_cpu_areas();
    smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
    build_all_zonelists(NULL, NULL);
    page_alloc_init();

    pr_notice("Kernel command line: %s\n", boot_command_line);
    parse_early_param();
    after_dashes = parse_args("Booting kernel",
                    static_command_line, __start___param,
                    __stop___param - __start___param,
                    -1, -1, NULL, &unknown_bootoption);
    if (!IS_ERR_OR_NULL(after_dashes))
        parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg);

    jump_label_init();

    /*
     * These use large bootmem allocations and must precede kmem_cache_init()
     */
    setup_log_buf(0);
    pidhash_init();
    vfs_caches_init_early();
    sort_main_extable();
    trap_init();
    mm_init();

    /*
     * Set up the scheduler prior starting any interrupts (such as the
     * timer interrupt). Full topology setup happens at smp_init()
     * time - but meanwhile we still have a functioning scheduler.
     */
    sched_init();
    /*
     * Disable preemption - early bootup scheduling is extremely
     * fragile until we cpu_idle() for the first time.
     */
    preempt_disable();
    if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))
        local_irq_disable();
    idr_init_cache();
    rcu_init();

    /* trace_printk() and trace points may be used after this */
    trace_init();

    context_tracking_init();
    radix_tree_init();
    /* init some links before init_ISA_irqs() */
    early_irq_init();
    init_IRQ();
    tick_init();
    rcu_init_nohz();
    init_timers();
    hrtimers_init();
    softirq_init();
    timekeeping_init();
    time_init();
    sched_clock_postinit();
    perf_event_init();
    profile_init();
    call_function_init();
    WARN(!irqs_disabled(), "Interrupts were enabled early\n");
    early_boot_irqs_disabled = false;
    local_irq_enable();
    kmem_cache_init_late();

    /*
     * HACK ALERT! This is early. We're enabling the console before
     * we've done PCI setups etc, and console_init() must be aware of
     * this. But we do want output early, in case something goes wrong.
     */
    console_init();
    if (panic_later)
        panic("Too many boot %s vars at `%s'", panic_later, panic_param);

    lockdep_info();

    /*
     * Need to run this when irqs are enabled, because it wants
     * to self-test [hard/soft]-irqs on/off lock inversion bugs
     * too:
     */
    locking_selftest();

#ifdef CONFIG_BLK_DEV_INITRD
    if (initrd_start && !initrd_below_start_ok &&
        page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
        pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
        page_to_pfn(virt_to_page((void *)initrd_start)), min_low_pfn);
        initrd_start = 0;
    }
#endif
    page_ext_init();
    debug_objects_mem_init();
    kmemleak_init();
    setup_per_cpu_pageset();
    numa_policy_init();
    if (late_time_init)
    late_time_init();
    sched_clock_init();
    calibrate_delay();
    pidmap_init();
    anon_vma_init();
    acpi_early_init();
#ifdef CONFIG_X86
    if (efi_enabled(EFI_RUNTIME_SERVICES))
        efi_enter_virtual_mode();
#endif
#ifdef CONFIG_X86_ESPFIX64
    /* Should be run before the first non-init thread is created */
    init_espfix_bsp();
#endif
    thread_stack_cache_init();
    cred_init();
    fork_init();
    proc_caches_init();
    buffer_init();
    key_init();
    security_init();
    dbg_late_init();
    vfs_caches_init();
    signals_init();
    /* rootfs populating might need page-writeback */
    page_writeback_init();
    proc_root_init();
    nsfs_init();
    cpuset_init();
    cgroup_init();
    taskstats_init_early();
    delayacct_init();
    check_bugs();

    acpi_subsystem_init();
    sfi_init_late();

    if (efi_enabled(EFI_RUNTIME_SERVICES)) {
        efi_late_init();
        efi_free_boot_services();
    }

    ftrace_init();

    /* Do the rest non-__init'ed, we're now alive */
    rest_init();
}

start_kernel()函數中執(zhí)行了大量的初始化操作:

  • setup_arch():主要做一些板級初始化理朋,cpu初始化,tag參數解析绿聘,u-boot傳遞的cmdline解析嗽上,建立mmu工作頁表,初始化內存布局熄攘,調用mmap_io建立GPIO兽愤、IRQ、MEMCTRL挪圾、UART浅萧,及其他外設的靜態(tài)映射表,對時鐘哲思,定時器洼畅,uart進行初始化
  • sched_init():初始化每個處理器的可運行隊列,設置系統(tǒng)初始化進程即0號進程
  • softirq_init():內核的軟中斷機制初始化函數
  • console_init():初始化系統(tǒng)的控制臺結構
  • rest_init():調用kernel_thread()創(chuàng)建1號內核線程棚赔,調用schedule()函數切換當前進程帝簇,在調用該函數之前,Linux系統(tǒng)中只有兩個進程靠益,即0號進程init_task和1號進程kernel_init丧肴,其中kernel_init進程也是剛剛被創(chuàng)建的。調用該函數后胧后,1號進程kernel_init將會運行

4.3 kernel進程

Linux下有3個特殊的進程芋浮,idle(swapper)進程(PID = 0)、init進程(PID = 1)和kthreadd(PID = 2)

  • idle(swapper)進程由系統(tǒng)自動創(chuàng)建绩卤,運行在內核態(tài)
    idle進程其pid=0途样,其前身是系統(tǒng)創(chuàng)建的第一個進程,也是唯一一個沒有通過fork或者kernel_thread產生的進程濒憋。
    完成加載系統(tǒng)后何暇,演變?yōu)檫M程調度、交換凛驮,常常被稱為交換進程裆站。
  • init進程由idle通過kernel_thread創(chuàng)建,在內核空間完成初始化后黔夭,加載init程序宏胯,并最終轉變?yōu)橛脩艨臻g的init進程
    由0進程創(chuàng)建,完成系統(tǒng)的初始化. 是系統(tǒng)中所有其它用戶進程的祖先進程本姥。
    Linux中的所有進程都是有init進程創(chuàng)建并運行的肩袍。首先Linux內核啟動,然后在用戶空間中啟動init進程婚惫,再啟動其他系統(tǒng)進程氛赐。
    在系統(tǒng)啟動完成后魂爪,init將變?yōu)槭刈o進程監(jiān)視系統(tǒng)其他進程。
  • kthreadd進程由idle通過kernel_thread創(chuàng)建艰管,并始終運行在內核空間滓侍,負責所有內核線程的調度和管理
    它的任務就是管理和調度其他內核線程kernel_thread,會循環(huán)執(zhí)行一個kthreadd的函數牲芋,該函數的作用就是運行kthread_create_list全局鏈表中維護的kthread撩笆,當我們調用kernel_thread創(chuàng)建的內核線程會被加入到此鏈表中,因此所有的內核線程都是直接或者間接的以kthreadd為父進程缸浦。

5. Init初始化介紹

init進程是Linux內核啟動后創(chuàng)建的第一個用戶空間的進程夕冲,init在初始化過程中會啟動很多重要的守護進程。

5.1 init啟動

代碼位于alps/system/core/init/init.cpp

init.cpp的mian函數入口同時也是ueventd和watchdogd守護進程的入口餐济,通過參數進行控制

int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }
    ...
}

默認情況下耘擂,一個進程創(chuàng)建出來的文件和文件夾屬性都是022,使用umask()函數能設置文件屬性的掩碼絮姆。參數為0意味著進程創(chuàng)建的文件屬性是0777。接著創(chuàng)建一些基本的目錄包括dev秩霍、proc篙悯、sys等,同時把分區(qū)mount到對應的目錄

// 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.
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
// Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));

init進程會調用property_init創(chuàng)建一個共享區(qū)域來存儲屬性值铃绒,初始化完后獲取kernel傳過來的cmdline去設置一些屬性鸽照,然后初始化SELinux和安全上下文。接著會通過property_load_boot_defaults去加載default.prop等文件初始化系統(tǒng)屬性

property_init();

// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
process_kernel_cmdline();

// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();

// Make the time that init started available for bootstat to log.
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

// Set libavb version for Framework-only OTA match in Treble build.
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);

// Clean up our environment.
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");

// Now set up SELinux for second stage.
selinux_initialize(false);
selinux_restore_context();

property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
set_usb_controller();

初始化屬性和SELinux后颠悬,接著解析init.rc的文件內容矮燎,通過init.rc相關語法配置和啟動進程以及啟動的順序

const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);

ActionManager& am = ActionManager::GetInstance();
ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();

parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
    parser.ParseConfig("/init.rc");
    parser.set_is_system_etc_init_loaded(
            parser.ParseConfig("/system/etc/init"));
    parser.set_is_vendor_etc_init_loaded(
            parser.ParseConfig("/vendor/etc/init"));
    parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
    parser.ParseConfig(bootscript);
    parser.set_is_system_etc_init_loaded(true);
    parser.set_is_vendor_etc_init_loaded(true);
    parser.set_is_odm_etc_init_loaded(true);
}

am.QueueEventTrigger("early-init");

// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");

// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");

// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
    am.QueueEventTrigger("charger");
} else {
    am.QueueEventTrigger("late-init");
}
    
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

main函數最后會進入一個死循環(huán),每次循環(huán)都會去調用ExecuteOneCommand執(zhí)行命令列表中的一條命令赔癌,如果服務掛了還會調用restart_processes重啟服務

while (true) {
    // By default, sleep until something happens.
    int epoll_timeout_ms = -1;

    if (do_shutdown && !shutting_down) {
        do_shutdown = false;
        if (HandlePowerctlMessage(shutdown_command)) {
            shutting_down = true;
        }
    }

    if (!(waiting_for_prop || sm.IsWaitingForExec())) {
        am.ExecuteOneCommand();
    }
    if (!(waiting_for_prop || sm.IsWaitingForExec())) {
        if (!shutting_down) restart_processes();

        // If there's a process that needs restarting, wake up in time for that.
        if (process_needs_restart_at != 0) {
            epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
            if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
        }

        // If there's more work to do, wake up again immediately.
        if (am.HasMoreCommands()) epoll_timeout_ms = 0;
    }

    epoll_event ev;
    int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
    if (nr == -1) {
        PLOG(ERROR) << "epoll_wait failed";
    } else if (nr == 1) {
        ((void (*)()) ev.data.ptr)();
    }
}

init進程初始化系統(tǒng)后诞外,會化身為守護進程來處理子進程的死亡信號、修改屬性的請求和組合鍵事件

5.2 init.rc

init.rc文件位于:alps/system/core/rootdir/init.rc

在init.cpp中灾票,啟動init.rc各個階段的順序是early_init > init > late_init峡谊,在late_init中又會去觸發(fā)其他階段的啟動,所以各個階段在init中啟動的順序如下:

early_init > init > late_init > early-fs > fs > post-fs > late_fs > post-fs-data > zygote-start > early-boot > boot

on late-init
    trigger early-fs
    trigger fs
    trigger post-fs
    trigger late-fs
    trigger post-fs-data
    trigger zygote-start
    trigger load_persist_props_action
    trigger firmware_mounts_complete
    trigger early-boot
    trigger boot

在boot階段會啟動class為hal和core的服務

on boot
    ...
    class_start hal
    class_start core

init.rc中支持的命令實現在builtins.cpp中刊苍,具體語法使用可以參考alps/system/core/init/README.md

const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    static const Map builtin_functions = {
        {"bootchart",               {1,     1,    do_bootchart}},
        {"chmod",                   {2,     2,    do_chmod}},
        {"chown",                   {2,     3,    do_chown}},
        {"class_reset",             {1,     1,    do_class_reset}},
        {"class_restart",           {1,     1,    do_class_restart}},
        {"class_start",             {1,     1,    do_class_start}},
        {"class_stop",              {1,     1,    do_class_stop}},
        {"copy",                    {2,     2,    do_copy}},
        {"domainname",              {1,     1,    do_domainname}},
        {"enable",                  {1,     1,    do_enable}},
        {"exec",                    {1,     kMax, do_exec}},
        {"exec_start",              {1,     1,    do_exec_start}},
        {"export",                  {2,     2,    do_export}},
        {"hostname",                {1,     1,    do_hostname}},
        {"ifup",                    {1,     1,    do_ifup}},
        {"init_user0",              {0,     0,    do_init_user0}},
        {"insmod",                  {1,     kMax, do_insmod}},
        {"installkey",              {1,     1,    do_installkey}},
        {"load_persist_props",      {0,     0,    do_load_persist_props}},
        {"load_system_props",       {0,     0,    do_load_system_props}},
        {"loglevel",                {1,     1,    do_loglevel}},
        {"mkdir",                   {1,     4,    do_mkdir}},
        {"mount_all",               {1,     kMax, do_mount_all}},
        {"mount",                   {3,     kMax, do_mount}},
        {"umount",                  {1,     1,    do_umount}},
        {"restart",                 {1,     1,    do_restart}},
        {"restorecon",              {1,     kMax, do_restorecon}},
        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
        {"rm",                      {1,     1,    do_rm}},
        {"rmdir",                   {1,     1,    do_rmdir}},
        {"setprop",                 {2,     2,    do_setprop}},
        {"setrlimit",               {3,     3,    do_setrlimit}},
        {"start",                   {1,     1,    do_start}},
        {"stop",                    {1,     1,    do_stop}},
        {"swapon_all",              {1,     1,    do_swapon_all}},
        {"symlink",                 {2,     2,    do_symlink}},
        {"sysclktz",                {1,     1,    do_sysclktz}},
        {"trigger",                 {1,     1,    do_trigger}},
        {"verity_load_state",       {0,     0,    do_verity_load_state}},
        {"verity_update_state",     {0,     0,    do_verity_update_state}},
        {"wait",                    {1,     2,    do_wait}},
        {"wait_for_prop",           {2,     2,    do_wait_for_prop}},
        {"write",                   {2,     2,    do_write}},
        {"set_meizu_props",         {0,     0,    do_set_meizu_props}},
    };
    // clang-format on
    return builtin_functions;
}

5.3 bootanim啟動

bootanim.rc定義了bootanim屬于core服務既们,但是設置了disable說明bootanim不是自啟動的服務,需要別的服務進行喚醒正什。

service bootanim /system/bin/bootanimation
    class core animation
    user graphics
    group graphics audio
    disabled
    oneshot
    writepid /dev/stune/top-app/tasks

5.4 surfaceflinger啟動

代碼里搜索bootanim啥纸,可以看到是surfaceflinger服務將bootanim啟動,surfaceflinger屬于core服務婴氮,自啟動服務斯棒,在init進程的on boot階段會啟動surfaceflinger馒索,surfaceflinger最后會啟動StartPropertySetThread從而啟動bootanim

service surfaceflinger /system/bin/surfaceflinger
    class core animation
    user system
    group graphics drmrpc readproc
    onrestart restart zygote
    writepid /dev/stune/foreground/tasks
    socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
    socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
    socket pdx/system/vr/display/vsync      stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0
bool StartPropertySetThread::threadLoop() {
    // Set property service.sf.present_timestamp, consumer need check its readiness
    property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
    // Clear BootAnimation exit flag
    property_set("service.bootanim.exit", "0");
    // Start BootAnimation if not started
    property_set("ctl.start", "bootanim");
    // Exit immediately
    return false;
}

surfaceflinger服務的main函數入口在main_surfaceflinger,主要操作有:

  1. 啟動Hidl服務名船,主要是DisplayService
  2. 啟動線程池
  3. 初始化SurfaceFlinger
  4. 將SurfaceFlinger和GpuService注冊到ServiceManager
  5. 啟動SurfaceFlinger線程
int main(int, char**) {
    startHidlServices();

    signal(SIGPIPE, SIG_IGN);
    // When SF is launched in its own process, limit the number of
    // binder threads to 4.
    ProcessState::self()->setThreadPoolMaxThreadCount(4);

    // start the thread pool
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();

    // instantiate surfaceflinger
    sp<SurfaceFlinger> flinger = new SurfaceFlinger();

    setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);

    set_sched_policy(0, SP_FOREGROUND);

    // Put most SurfaceFlinger threads in the system-background cpuset
    // Keeps us from unnecessarily using big cores
    // Do this after the binder thread pool init
    if (cpusets_enabled()) set_cpuset_policy(0, SP_SYSTEM);

    // initialize before clients can connect
    flinger->init();

    // publish surface flinger
    sp<IServiceManager> sm(defaultServiceManager());
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);

    // publish GpuService
    sp<GpuService> gpuservice = new GpuService();
    sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false);

    struct sched_param param = {0};
    param.sched_priority = 2;
    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
        ALOGE("Couldn't set SCHED_FIFO");
    }

    // run surface flinger in this thread
    flinger->run();

    return 0;
}

surfaceflinger繼承了Thread绰上,執(zhí)行run方法后,本質上是調用c++中的pthread類渠驼,線程入口函數是threadLoop蜈块,threadLoop的含義是通過一個循環(huán)不斷的調用該函數,當threadLoop返回false的時候退出循環(huán)

由于bootanim的threadLoop返回false迷扇,所以啟動函數在開機過程中只會執(zhí)行一次

接下來的分析請看Android啟動流程簡析(二)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末百揭,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子蜓席,更是在濱河造成了極大的恐慌器一,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厨内,死亡現場離奇詭異祈秕,居然都是意外死亡,警方通過查閱死者的電腦和手機雏胃,發(fā)現死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門请毛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瞭亮,你說我怎么就攤上這事方仿。” “怎么了统翩?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵仙蚜,是天一觀的道長。 經常有香客問我厂汗,道長委粉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任面徽,我火速辦了婚禮艳丛,結果婚禮上,老公的妹妹穿的比我還像新娘趟紊。我一直安慰自己氮双,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布霎匈。 她就那樣靜靜地躺著戴差,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铛嘱。 梳的紋絲不亂的頭發(fā)上暖释,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天袭厂,我揣著相機與錄音,去河邊找鬼球匕。 笑死纹磺,一個胖子當著我的面吹牛,可吹牛的內容都是我干的亮曹。 我是一名探鬼主播橄杨,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼照卦!你這毒婦竟也來了式矫?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤役耕,失蹤者是張志新(化名)和其女友劉穎采转,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體瞬痘,經...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡故慈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了图云。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惯悠。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖竣况,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情筒严,我是刑警寧澤丹泉,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站鸭蛙,受9級特大地震影響摹恨,放射性物質發(fā)生泄漏。R本人自食惡果不足惜娶视,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一晒哄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肪获,春花似錦寝凌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至青柄,卻和暖如春伐债,著一層夾襖步出監(jiān)牢的瞬間预侯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工峰锁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萎馅,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓虹蒋,卻偏偏與公主長得像糜芳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子千诬,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345