computer_knowledge_notes/OS/Openthos/启动过程.md

24 KiB
Raw Blame History

step1: boot rom

执行固化在rom中的指令把boot loader加载到内存跳转到boot loader继续执行。

step2: boot loader

源代码在bootable目录。也是OEM厂商或运营商加锁和限制的地方。分为两个阶段。

第一阶段检测ram加载第二阶段的程序。

第二阶段:设置网络,内存等。

step3: Linux kernel

源代码在kernel目录。内核启动时设置缓存、被保护存储器、计划列表加载驱动。当内核完成系统设置它首先在系统文件中寻找”init”文件然后启动root进程或者系统的第一个进程。

  • init/main.c : start_kernel

  • init/main.c : rest_init

  • init/main.c : kernel_init

    ramdisk_execute_command是内核参数rdinit的值,默认为/init

  • init/main.c : run_init_process

    init进程分两个阶段第一阶段在内核态通过执行kernel_execve函数强行转到用户态,第二阶段在用户态继续执行。

step4: init process

源代码在system/core/init/init.cinit.rc在system/core/rootdir/init.rc。

init是第一个进程也叫root进程或所有进程的父进程。在此阶段可以看到屏幕上的"Android" Logo。

init进程的职责
  1. 挂载目录,比如/sys, /dev, /porc。

  2. 运行init.rc脚本这会创建系统中的几个关键进程包括java世界的开创者zygote进程也是在这里创建的。脚本语言的介绍详见system/core/init/README.md。

  3. 作为守护进程。

  4. 属性服务。

  5. 处理子进程终止。

    详见queue_builtin_action(signal_init_action, "signal_init")

init进程第一阶段
  1. 依据第一个参数进行跳转,并设置环境变量

    如果第1个参数是ueventd则执行ueventd_main,进行设备节点的创建和权限设定。

    如果第1个参数是watchdogd则执行watchdogd_main,用于系统出问题时重启系统。

    如果REBOOT_BOOTLOADER_ON_PANICinstall_reboot_signal_handlers将一些信号的标志设置为SA_RESTART一旦监听到这些信号则执行重启操作。

    在当前环境中加入PATH变量。

    由于INIT_SECOND_STAGE是在is_first_stage里定义的这就确保了is_first_stage只能执行一次。

  2. 挂载文件系统并创建目录

    umask(0)用于清空文件权限。

    在根目录上建立initramdisk所需的基本文件系统文件系统的其它部分是在随后的rc文件里建立的

    mount的第一个参数是源(通常是一个设备名),第二个参数是所要挂载的目标目录,第三个参数是文件系统类型,第四个参数是读写标记(MS_NOSUID表示执行时不遵照set-user-id位和set-group-id位),第五个参数是要挂载的文件系统可能会有的特有参数。

    mount挂载了如下五个源:tmpfs是内存中的文件系统,读写速度快。proc是内存中内核的数据结构。devpts为伪终端提供了一个标准接口。sysfs是把proc, devfs, devpts统一起来。selinuxfs也是虚拟文件系统用来存放SELinux安全策略文件。

    mknode用于创建设备文件。第一个参数是设备所在目录第二个参数是设备类型和读写访问标志S_IFCHR表示字符设备文件第三个参数是设备(设备由makedev(a,b)创建其中a为次设备号b为主设备号)。

  3. 初始化日志输出,挂载分区设备

    InitKernelLogging用于初始化日志系统它首先把输入输出重定向到/sys/fs/selinux/null然后调用InitLogging初始化日志系统。

    DoFirstStageMount用于初始化特定设备并挂载。

  4. 启用SELinux安全策略

SetInitAvbVersionInRecovery()在刷机模式下初始化avb的版本,不是刷机模式直接跳过。

Avb即Android Verfied boot,功能包括Secure Boot, verfying boot 和 dm-verity, 原理都是对二进制文件进行签名在系统启动时进行认证确保系统运行的是合法的二进制镜像文件。其中认证的范围涵盖bootloaderboot.imgsystem.img

selinux_initialize(true)加载SELinux policy也就是安全策略。

SELinux是「Security-Enhanced Linux」的简称是美国国家安全局和SCC开发的 Linux的一个扩张强制访问控制安全模块。 在这种访问控制体系的限制下,进程只能访问那些在他的任务中所需要文件。

现在是在内核态所以在SELinux policy加载后需要再次执行init转换到用户态。函数selinux_android_restorecon("/init", 0)就是做这个工作的。

  1. 开始第二阶段前的准备

    设置环境变量INIT_SECOND_STAGE和INIT_STARTED_AT为第二阶段做准备。

    execv(path,args)再次执行init进入用户态的init进程。

init进程第二阶段
  1. 创建进程会话密钥并初始化系统属性

    InitKernelLogging用于初始化日志系统第一阶段在内核态而第二阶段在用户态所以要再次初始化日志系统

    keyctl_get_keyring_ID用于初始化进程会话密钥。KEY_SPEC_SESSION_KEYRING表示获取当前进程的会话密钥1表示如获取不到则创建一个。

    /dev目录下创建文件.booting表示booting正在进行中(可以在init.rc里看到当booting结束会删除此文件)。

    property_init用于初始化属性系统并从指定文件读取属性。

    property_init调用__system_property_area_init实现其功能。

    process_kernel_dt和process_kernel_cmdline分别处理dt(设备树)和命令行属性,设备树中参数的优先级总是高于命令行。

    export_kernel_boot_props用于处理其它的一些属性。

    分别将INIT_STARTED_AT、INIT_SELINUX_TOOK和INIT_AVB_VERSION的值设置为系统属性都是通过property_set实现的property_set就是用来设置系统属性的

    使用unsetenv清空若干环境变量因为这些环境变量已经存入到系统属性中去了。

  2. 设置第二阶段的SELinux

    selinux_initialize(false)执行第二阶段的selinux初始化在这里参数false只起个开关的作用表明执行的是第二阶段的初始化。

    selinux_restore_context用于恢复安全上下文。

  3. 新建epoll并初始化子进程终止信号处理函数

    epoll_create1用于创建epoll实例并返回epoll的文件描述符。epoll是Linux用来做事件触发的。

    signal_handler_init且来注册signal信号的处理函数。

  4. 设置其他系统属性并开启系统属性服务

    property_load_boot_defaults从文件中加载一些属性。

    export_oem_lock_status设置ro.boot.flash.locked属性。

    start_property_service开启一个socket监听系统属性的设置。

    set_usb_controller设置sys.usb.controller属性。

init进程第二阶段之解析rc文件
  1. 解析rc文件

    set_function_map把function_map存放到Action中作为成员属性。

    创建对server、action、import的解析器。parser.AddSectionParser用来new一个Parser(解析器)然后把它们放到一个map里存起来。

    获取ro.boot.init_rc的属性值如其值为空则解析/init.rc/{system,vendor,odm}/etc/init下的rc文件如非空则解析其属性值。

    parser.ParseConfig()是用来解析rc文件的其定义在init/init_parser.cpp文件。它就是判断参数是不是目录如果是目录则调用ParseConfigDir()如果是文件则调用ParseConfigFile()。

    ParseConfigFile()首先用ReadFile()把数据读取到data然后调用ParseData来解析data最后遍历section_parsers_并依次调用其EndFile()。

  2. 加入一些事件和Action

    am.QueueEventTrigger("early-init")用于触发early-init事件。

    am.QueueBuiltinAction用于添加Action第一个参数是要执行的command第二个参数是trigger。

    am.QueueEventTrigger("init")用来触发所有的boot actions。

    bootmode是个全局静态变量用来标示启动模式android的启动模式分为正常开机流程和关机充电流程。

  3. 触发所有的事件并不断监听新的事件

    之前的工作是建立各种各样的事件并没有去触发。只有在本while循环里才真正的触发所有的事件并用epoll不断监听新的事件。

    如要执行关机命令,则通过HandlePowerctlMessage()来执行之。

    当没有属性设置且没有Server开始运行时才通过am.ExecuteOneCommand()从current_executing_actions_列表里执行一个command由于外部是一个无限循环所以current_executing_actions_列表里的每个command都会执行一遍。这样的条件设定是为了保证属性设置和Server启动的完整性。

    当没有属性设置且没有Server开始运行时

    • 如系统不是正在关机,执行restart_processes()来判断当前Server是否需要重启如需重启则执行重启操作。
    • 如有进程需要重启,设置epoll_timeout_ms的值
    • 如有其它command需要执行则将epoll_timeout_ms设置为0以立即唤醒。

    epoll_wait用于监听事件的产生,参数epoll_fd是由前面的epoll_create1()创建的epoll的文件描述符参数ev用于保存事件信息,参数epoll_timeout_ms是等待时间

    如未监听到事件则向log系统添加一条监听失败的信息如监听到事件则直接执行ev.data.ptr()即epoll_ctl注册时的回调函数

step5: Zygote

源代码在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

Zygote是一个虚拟机进程由init进程启动。Zygote预加载以及初始化核心库类。

在此阶段可以看到启动画面。

Zygote进程触发过程
  • init.rc里import /init.{ro.zygote}.rc引入了Zygote进程

    ro.zygote可能取4个值zygote32为纯32位模式zygote64为纯64位模式zygote32_64以32位为主模式zygote64_32以64位为主模式

  • init.zygote64_32.rc定义了两个进程zygote和zygote_secondary

    zygote程序是app_process64zygote_secondary程序是app_process32。两者的参数和属性基本相同。

  • 在init.cpp的main里触发late-init在init.rc的late-init里触发zygote-start在init.rc的zygote-start里启动zygote进程和zygote_secondary进程。

  • 在init/builtins.cpp的builtin_functions里定义了start命令对应的函数do_startdo_start函数最终是调用对应server的Start方法Start方法主要是fork一个新进程然后执行对应server的二进制文件。

  • zygote进程的源码在frameworks/base/cmds/app_process/app_main.cpp

Zygote进程之参数解析

app_main.cpp里的main函数主要做的就是参数解析。此函数有两种启动模式一是作为Zygote进程进行初始化一是作为普通应用程序启动。两者最终都是调用AppRuntime对象的start函数加载对应的java类(ZygoteInit或RuntimeInit),并将之前整理的参数传入进去。

  1. 将参数argv[]放入字符串argv_string
  2. 创建AppRuntime对象runtime并传入除argv[0]的所有参数
  3. '--'之前或以非'-'开头的参数都将传入vm(即/system/bin)vm参数后的第一个参数(即Xzygote)不再使用。--zygote的意思是开启zygote模式--start-system-server的意思是开启系统服务--socket-name指定socket的名称。必须复制参数字符串但进入vm的spaced commands是个例外。
  4. spaced_commands里定义的两个参数是Java程序需要依赖的Jar包
  5. 使用runtime.addOption方法加入参数要加入的都是'--'开头的参数但若包含spaced_commands里的参数则直接加入
  6. 判断zygote, niceName, startSystemServer, application, niceName.setTo, className.setTo的值它们主要决定了启动模式。
  7. 如果className非空表明不是zygote模式只需传送程序参数即可。否则就是zygote模式则创建Dalvik缓存加入start-system-server参数加入abiFlag参数加入剩下的参数。
  8. 如果niceName非空则设置进程别名。
  9. 如果是zygote模式则加载ZygoteInit如果是application模式则加载RuntimeInit否则打印错误。
创建虚拟机

frameworks/base/core/jni/AndroidRuntime.cpp里的start方法

  1. 打印一些日志设置环境变量ANDROID_ROOT。

  2. jni_invocation.Init初始化JNI

    首先通过dlopen加载libart.so获得其句柄然后调用dlsym从libart.so中找到相关函数用于后续的虚拟机创建。

  3. startVm创建虚拟机

    一是从各种系统属性中读取一些参数然后通过addOption设置到AndroidRuntime的mOptions数组二是使用libart.so中的JNI_CreateJavaVM将这些参数传入。

  4. onVmCreated表示创建完成

  5. startReg注册JNI函数

    首先设置了Android创建线程的处理函数然后创建了一个局部引用作用域最后调用refister_jni_procs进行JNI注册

  6. JNI方式调用ZygoteInit类的main函数

JNI反射调用Java

frameworks/base/core/jni/AndroidRuntime.cpp里的start方法

  1. 首先把参数保存在strArray里
  2. 启动VM此线程将成为VM的主线程此线程不会返回除非VM退出。
ZygoteInit

framework/base/core/java/com/android/internal/os/ZygoteInit.java的main方法

  1. 创建一个ZygoteServer对象
  2. 设置标记不允许新建线程
  3. 设置Zygote进程的组id
  4. 性能统计(在一个try代码块里的内容)
    • 把Zygote启动时间报告给tron(使用MetricsLogger.histogram)
    • 跟踪调试ZygoteInit(bootTimingsTraceLog.traceBegin)
    • 注册Ddms的处理类(RuntimeInit.enableDdms)
    • 在一个for循环里进行参数解析
    • 在某些配置里,要避免过早地加载资源和类
    • 初始化垃圾回收
    • 禁止追踪
    • Zygote解除挂载根空间
    • 设置seccomp策略
    • 在zygote永远进行select循环
  5. try代码块执行到最后要执行finally代码块的内容关闭zygoteServer的套接字。
  6. 已经退出了select循环进入了子进程继续执行命令caller.run()

step6: System Servers

Zygote启动系统服务即ZygoteInit类里的startSystemServer方法。

step7: 引导完成

系统服务运行起来后,引导完成,此时会发送开机启动广播ACTION_BOOT_COMPLETED

附录Android Init Language

详见system/core/init/README.md

由五类语句构成Actions, Commands, Services, Options and Imports。

从整体来看一个rc文件是由若干个段组成的。一个段只能是Actions或Server其中之一

  • Actions其中Commands和trigger是对Actions的补充。
  • Servers其中Options是对Servers的补充。
.rc文件

init language用在.rc文件中它可能出现在系统中的多个位置。

  • /init.rc是最初的.rc文件它会随着init的执行而执行用于系统的最初设置。
Actions

Actions就是触发器加一串命令触发器用来决定是否执行一个Action。如果满足了触发器的条件且action还不在运行队列里则这个action会被加到运行队列的尾部。

运行的格式如下:

on <trigger> [&& <trigger>]*
  <command>
  <command>

以on开头后面的trigger是判断条件(详见Triggers)command是具体要执行的操作。

Services

Services是程序它由init进程启动并且可以退出后重启(可选)。

服务的格式如下:

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

name是程序名pathname是程序所在的路径argument是程序的参数option是服务配置(详见Options)。

Options

Options是Services的修改器它们影响init进程如何运行service以及何时运行service。

  • console此服务需要一个console。默认设备为/dev/console。也可自己指定,如console /dev/tty0,其中/dev/是可忽略的,即等同于console tty0
  • ctitical此服务是一个设备关键服务。如果在4分钟内退出4次设备将会重启进入recovery模式。
  • disabled此服务无法通过它的类来启动必须通过它的名字来启动。
  • setenv在启动的进程里设置环境变量。
  • socket创建一个unix域socket并将它的fd传给启动的进程。
  • file打开一个文件并将它的fd传给启动的进程。
  • user在执行服务前改变用户名。
  • group在执行服务前改变组名。
  • capabilities在执行服务的时候设置capabilities.
  • seclabel在执行服务前改变为seclabel。
  • oneshot当服务退出后不要重启它。
  • class为服务指定所属的类。在同一个类里的服务可以同时打开或关闭。如不使用此选项明确指定服务所属的类则默认在default类里。
  • animation将会包含所有启动和关闭animation所必须的服务。
  • onrestart当服务重启的时候执行一条命令。
  • writepid当子进程forks的时候将其pid写到给定文件中。
  • priority调度service进程的优先级。
  • namespace当forking一个服务的时候进入一个新的PID或挂载namespace。
  • oom_score_adjust设置子进程的变量值。
  • memcg.swappiness设置子进程的变量值。
  • memcg.soft_limit_in_bytes设置子进程的变量值。
  • memcg.limit_in_bytes设置子进程的变量值。
  • shutdown设置关机的时候此service进程的行为。默认是关机进程通过传递SIGTERM和SIGKILL来杀死此服务。
Triggers

Triggers就是个字符串用来匹配特定事件和触发一个action。分为事件触发器和权限触发器。

事件触发器就是个简单的字符串,如"boot"或"late-init"。

权限触发器就是个键值对,其格式为property:<name>=<value>

一个动作可以有多个权限触发器但只能有一个事件触发器。

Commands

bootchart [start|stop]

Start/stop bootcharting. These are present in the default init.rc files, but bootcharting is only active if the file /data/bootchart/enabled exists; otherwise bootchart start/stop are no-ops.

chmod <octal-mode> <path>

Change file access permissions.

chown <owner> <group> <path>

Change file owner and group.

class_start <serviceclass>

Start all services of the specified class if they are not already running. See the start entry for more information on starting services.

class_stop <serviceclass>

Stop and disable all services of the specified class if they are currently running.

class_reset <serviceclass>

Stop all services of the specified class if they are currently running, without disabling them. They can be restarted later using class_start.

class_restart <serviceclass>

Restarts all services of the specified class.

copy <src> <dst>

Copies a file. Similar to write, but useful for binary/large amounts of data. Regarding to the src file, copying from symbolic link file and world-writable or group-writable files are not allowed. Regarding to the dst file, the default mode created is 0600 if it does not exist. And it will be truncated if dst file is a normal regular file and already exists.

domainname <name>

Set the domain name.

enable <servicename>

Turns a disabled service into an enabled one as if the service did not specify disabled. If the service is supposed to be running, it will be started now. Typically used when the bootloader sets a variable that indicates a specific service should be started when needed. E.g.

on property:ro.boot.myfancyhardware=1 enable my_fancy_service_for_my_fancy_hardware

exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]

Fork and execute command with the given arguments. The command starts after "--" so that an optional security context, user, and supplementary groups can be provided. No other commands will be run until this one finishes. seclabel can be a - to denote default. Properties are expanded within argument. Init halts executing commands until the forked process exits.

exec_start <service>

Start a given service and halt the processing of additional init commands until it returns. The command functions similarly to the exec command, but uses an existing service definition in place of the exec argument vector.

export <name> <value>

Set the environment variable name equal to value in the global environment (which will be inherited by all processes started after this command is executed)

hostname <name>

Set the host name.

ifup <interface>

Bring the network interface interface online.

insmod [-f] <path> [<options>]

Install the module at path with the specified options. -f: force installation of the module even if the version of the running kernel and the version of the kernel for which the module was compiled do not match.

load_all_props

Loads properties from /system, /vendor, et cetera. This is included in the default init.rc.

load_persist_props

Loads persistent properties when /data has been decrypted. This is included in the default init.rc.

loglevel <level>

Sets the kernel log level to level. Properties are expanded within level.

mkdir <path> [mode] [owner] [group]

Create a directory at path, optionally with the given mode, owner, and group. If not provided, the directory is created with permissions 755 and owned by the root user and root group. If provided, the mode, owner and group will be updated if the directory exists already.

mount <type> <device> <dir> [ <flag>\* ] [<options>]

Attempt to mount the named device at the directory dir _flag_s include "ro", "rw", "remount", "noatime", ... options include "barrier=1", "noauto_da_alloc", "discard", ... as a comma separated string, eg: barrier=1,noauto_da_alloc

restart <service>

Stops and restarts a running service, does nothing if the service is currently restarting, otherwise, it just starts the service.

restorecon <path> [ <path>\* ]

Restore the file named by path to the security context specified in the file_contexts configuration. Not required for directories created by the init.rc as these are automatically labeled correctly by init.

restorecon_recursive <path> [ <path>\* ]

Recursively restore the directory tree named by path to the security contexts specified in the file_contexts configuration.

rm <path>

Calls unlink(2) on the given path. You might want to use "exec -- rm ..." instead (provided the system partition is already mounted).

rmdir <path>

Calls rmdir(2) on the given path.

setprop <name> <value>

Set system property name to value. Properties are expanded within value.

setrlimit <resource> <cur> <max>

Set the rlimit for a resource.

start <service>

Start a service running if it is not already running. Note that this is not synchronous, and even if it were, there is no guarantee that the operating system's scheduler will execute the service sufficiently to guarantee anything about the service's status.

This creates an important consequence that if the service offers functionality to other services, such as providing a communication channel, simply starting this service before those services is not sufficient to guarantee that the channel has been set up before those services ask for it. There must be a separate mechanism to make any such guarantees.

stop <service>

Stop a service from running if it is currently running.

swapon_all <fstab>

Calls fs_mgr_swapon_all on the given fstab file.

symlink <target> <path>

Create a symbolic link at path with the value target

sysclktz <mins_west_of_gmt>

Set the system clock base (0 if system clock ticks in GMT)

trigger <event>

Trigger an event. Used to queue an action from another action.

umount <path>

Unmount the filesystem mounted at that path.

verity_load_state

Internal implementation detail used to load dm-verity state.

verity_update_state <mount-point>

Internal implementation detail used to update dm-verity state and set the partition.mount-point.verified properties used by adb remount because fs_mgr can't set them directly itself.

wait <path> [ <timeout> ]

Poll for the existence of the given file and return when found, or the timeout has been reached. If timeout is not specified it currently defaults to five seconds.

wait_for_prop <name> <value>

Wait for system property name to be value. Properties are expanded within value. If property name is already set to value, continue immediately.

write <path> <content>

Open the file at path and write a string to it with write(2). If the file does not exist, it will be created. If it does exist, it will be truncated. Properties are expanded within content.

Imports

解析init配置文件扩展当前配置。

参考文档