Android启动流程简析(一)

作者: 逸先森 | 来源:发表于2018-09-11 17:03 被阅读194次

    最近一时兴起,想对Android的启动流程进行一次分析,经过一番整理,从以下几个方面进行总结,代码部分只讨论思路,不论细节。

    1. Android架构介绍
    2. Android启动概述
    3. BootLoader介绍
    4. Kernel初始化介绍
    5. Init初始化介绍
    6. Zygote启动介绍
    7. SystemServer启动介绍
    8. Launcher启动介绍
    9. Log抓取与分析方法

    由于发表文章的时候提示内容过长无法发布,于是把文章拆成了三部分发布:

    1. Android启动流程简析(一)
    2. Android启动流程简析(二)
    3. Android启动流程简析(三)

    1. Android架构介绍

    Android的架构可以从架构图得知,主要分四层:

    Android经典的四层架构图 Android架构图

    每一层的作用不做介绍,这里主要讲涉及的镜像有boot.img、system.img、vendor.img、recovery.img、userdata.img、cache.img,与平台相关的镜像有lk.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 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。

    调用流程:
    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初始化线程系统
    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,就创建出一个名为bootstrap2的线程,然后运行这个线程。退出临界区,开中断;如果定义了ENABLE_NANDWRITE,在timer_init之后将执行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启动
      • 获取分区表信息
      • 判断电池电压是否过低,过低则进入预充电
      • 和tz通信
      • 初始化emmc或ufs中的rpmb用户加解密认证分区
      • 运行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决定,内核启动的执行的第一条的代码在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()函数中执行了大量的初始化操作:

    • setup_arch():主要做一些板级初始化,cpu初始化,tag参数解析,u-boot传递的cmdline解析,建立mmu工作页表,初始化内存布局,调用mmap_io建立GPIO、IRQ、MEMCTRL、UART,及其他外设的静态映射表,对时钟,定时器,uart进行初始化
    • sched_init():初始化每个处理器的可运行队列,设置系统初始化进程即0号进程
    • softirq_init():内核的软中断机制初始化函数
    • console_init():初始化系统的控制台结构
    • rest_init():调用kernel_thread()创建1号内核线程,调用schedule()函数切换当前进程,在调用该函数之前,Linux系统中只有两个进程,即0号进程init_task和1号进程kernel_init,其中kernel_init进程也是刚刚被创建的。调用该函数后,1号进程kernel_init将会运行

    4.3 kernel进程

    Linux下有3个特殊的进程,idle(swapper)进程(PID = 0)、init进程(PID = 1)和kthreadd(PID = 2)

    • idle(swapper)进程由系统自动创建,运行在内核态
      idle进程其pid=0,其前身是系统创建的第一个进程,也是唯一一个没有通过fork或者kernel_thread产生的进程。
      完成加载系统后,演变为进程调度、交换,常常被称为交换进程。
    • init进程由idle通过kernel_thread创建,在内核空间完成初始化后,加载init程序,并最终转变为用户空间的init进程
      由0进程创建,完成系统的初始化. 是系统中所有其它用户进程的祖先进程。
      Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。
      在系统启动完成后,init将变为守护进程监视系统其他进程。
    • kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间,负责所有内核线程的调度和管理
      它的任务就是管理和调度其他内核线程kernel_thread,会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread,当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程。

    5. Init初始化介绍

    init进程是Linux内核启动后创建的第一个用户空间的进程,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);
        }
        ...
    }
    

    默认情况下,一个进程创建出来的文件和文件夹属性都是022,使用umask()函数能设置文件属性的掩码。参数为0意味着进程创建的文件属性是0777。接着创建一些基本的目录包括dev、proc、sys等,同时把分区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创建一个共享区域来存储属性值,初始化完后获取kernel传过来的cmdline去设置一些属性,然后初始化SELinux和安全上下文。接着会通过property_load_boot_defaults去加载default.prop等文件初始化系统属性

    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函数最后会进入一个死循环,每次循环都会去调用ExecuteOneCommand执行命令列表中的一条命令,如果服务挂了还会调用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进程初始化系统后,会化身为守护进程来处理子进程的死亡信号、修改属性的请求和组合键事件

    5.2 init.rc

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

    在init.cpp中,启动init.rc各个阶段的顺序是early_init > init > late_init,在late_init中又会去触发其他阶段的启动,所以各个阶段在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,执行run方法后,本质上是调用c++中的pthread类,线程入口函数是threadLoop,threadLoop的含义是通过一个循环不断的调用该函数,当threadLoop返回false的时候退出循环

    由于bootanim的threadLoop返回false,所以启动函数在开机过程中只会执行一次

    接下来的分析请看Android启动流程简析(二)

    相关文章

      网友评论

        本文标题:Android启动流程简析(一)

        本文链接:https://www.haomeiwen.com/subject/cgvugftx.html