android加载efi分区,Android启动流程简析(一)

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

Android架构介绍

Android启动概述

BootLoader介绍

Kernel初始化介绍

Init初始化介绍

Zygote启动介绍

SystemServer启动介绍

Launcher启动介绍

Log抓取与分析方法

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

1. Android架构介绍

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

657336b545bd

Android经典的四层架构图

657336b545bd

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主要流程:

调用thread_init_early初始化线程系统

调用arch_early_init中判断如果存在mmu就初始化,设置异常向量基地址,使能中断相关寄存器

在platform_early_init中完成初始化硬件时钟、手机的主板等操作,这个函数每种cpu的实现都不一样,定义在bootable\bootloader\lk\platform{cpu型号}\platform.c下

target_early_init中完成初始化uart端口的操作,这个函数的实现在bootable\bootloader\lk\target{cpu型号}\init.c

调用函数heap_init完成内核堆栈的初始化,用与kmalloc等函数的内存分配

在thread_init函数中初始化定时器

调用timer_init初始化内核定时器

如果没有定义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阶段:

arch_init主要是打印一些信息

target_init主要完成的操作有

从共享内存中读写xbl提供的pmic信息

初始化spmi总线,用于cpu和pmic通信

初始化ap与rpm通信通道

初始化按键

判断内核是否签名,当使用的是签名的内核时,需要初始化加密解密引擎

判断是从usf还是emmc启动

获取分区表信息

判断电池电压是否过低,过低则进入预充电

和tz通信

初始化emmc或ufs中的rpmb用户加解密认证分区

运行keymaster

apps_init主要完成一些应用功能的初始化,并调用aboot_init

3.4 aboot_init

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

根据target_is_emmc_boot()判断是否是从emmc存储设备上启动,然后分别获取对应存储设备的页大小和页掩码

取得设备的device_info信息,保存到device变量中

初始化lcd驱动,显示手机开机后的第一副图片

获取emmc或者flash芯片的产品序列号,最后在启动kernel时通过cmdline中的androidboot.serialno参数传给内核

检查按键判断是进入recovery还是fastboot

检查重启模式

跳转到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

#include

.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(&sm));

parser.AddSectionParser("on", std::make_unique(&am));

parser.AddSectionParser("import", std::make_unique(&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<: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,主要操作有:

启动Hidl服务,主要是DisplayService

启动线程池

初始化SurfaceFlinger

将SurfaceFlinger和GpuService注册到ServiceManager

启动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 ps(ProcessState::self());

ps->startThreadPool();

// instantiate surfaceflinger

sp 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 sm(defaultServiceManager());

sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);

// publish GpuService

sp 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,所以启动函数在开机过程中只会执行一次

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值