欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

[點(diǎn)晴永久免費(fèi)OA]Android 開(kāi)機(jī)流程介紹

freeflydom
2024年1月31日 18:58 本文熱度 794

 

一、目的

        從2014年Android4.0開(kāi)始接觸機(jī)器人,開(kāi)發(fā)過(guò)App應(yīng)用軟件,研究過(guò)Framework層框架結(jié)構(gòu)、也梳理過(guò)Native層的系統(tǒng)流程,但是對(duì)于Hal層,以及底下的kernel方向,知之甚少。
        本著一份對(duì)于底層知識(shí)的渴望,也為方便以后debug問(wèn)題,故開(kāi)始嘗試分析下Android的開(kāi)機(jī)流程。

二、環(huán)境

  1. 版本:Android 12

  2. 平臺(tái):展銳 SPRD8541E

  3. kernel:5.4

三、相關(guān)概念

3.1 Android平臺(tái)架構(gòu)

        如下是Google官網(wǎng)提供的平臺(tái)架構(gòu)圖,很直觀地攘括了Android的層級(jí),關(guān)于各個(gè)層級(jí)的結(jié)構(gòu),有想了解可以參考:https://developer.android.google.cn/guide/platform?hl=zh-cn

ps: Android系統(tǒng)為什么要有Hal層?

3.2 Android啟動(dòng)架構(gòu)

        如下是從某大佬的文章里摘取的圖片,很詳細(xì)地描述了Android系統(tǒng)啟動(dòng)過(guò)程:Boot Loader引導(dǎo)開(kāi)機(jī) -> Kernel -> Native -> Framework -> App

3.3 zImage

        zImage是一般情況下默認(rèn)的壓縮內(nèi)核映像文件,壓縮vmlinux,加上一段解壓?jiǎn)?dòng)代碼得到,只能從0X0地址運(yùn)行。

3.4 RAMDISK

        RAMDISK(initrd) 是一個(gè)小的分區(qū)像,在引導(dǎo)時(shí)內(nèi)核以只讀方式掛載它。它只保護(hù)/int和一些置文件,它用于初始化和掛載其它的文件系統(tǒng)鏡像。
        ramdisk.img被包含Google android SDK中(SSDK ROOT/toolslibimages/ramdiskimg) , 也可以編生成(SSDK ROOT/outarget/productSPRODUT NAME/ramdisk.img) 。這是一個(gè)gzip壓縮的CPIO文件。

3.5 RC文件

        rc文件,是用Android Init Language編寫(xiě)的特殊文件。用這種語(yǔ)法編寫(xiě)的文件,統(tǒng)一用".rc"后綴。所有rc文件,不會(huì)被編譯/鏈接。它是配置文件,不是程序,是一種用于android init的配置文件。真正加載rc文件,并進(jìn)行解析,做事情的是 Init進(jìn)程。

四、詳細(xì)設(shè)計(jì)

4.1 Boot Rom

        當(dāng)長(zhǎng)按電源開(kāi)機(jī)的時(shí)候,引導(dǎo)芯片開(kāi)始從固化在ROM的預(yù)設(shè)代碼開(kāi)始執(zhí)行,然后將加載引導(dǎo)程序到RAM中。

4.2 BootLoader

        BootLoader又稱為引導(dǎo)程序,它在運(yùn)行操作系統(tǒng)之前運(yùn)行的一段程序,是運(yùn)行的第一個(gè)程序。主要的功能有檢查RAM、初始化一些硬件外設(shè)等功能,它最終的目的是啟動(dòng)操作系統(tǒng)。

4.3 Kernel

        Kernel初始化可以分成三部分:zImage解壓縮、kernel的匯編啟動(dòng)階段、Kernel的C啟動(dòng)階段

4.3.1 zImage解壓縮階段

  1. 內(nèi)核加載到ram(內(nèi)存)后,內(nèi)核映像并不能直接運(yùn)行,它是一個(gè)壓縮的zImage文件。

  2. zImage映像中的并非一切被壓縮,映像中包含未被壓縮部分,這部分中包含解壓縮程序,解壓縮程序會(huì)解壓縮映像中被壓縮的部分。

  3. zImage使用gzip壓縮的,它不僅僅是一個(gè)壓縮文件,而且在這個(gè)文件的開(kāi)頭部分內(nèi)嵌有g(shù)zip解壓縮代碼。

  4. 當(dāng)zImage被調(diào)用時(shí)它從arch/arm/boot/compressed/head.S的start匯編例程開(kāi)始執(zhí)行,并最終調(diào)用arch/arm/boot/compressed/misc.c中的decompress_kernel()解壓縮內(nèi)核。

4.3.2 kernel的匯編啟動(dòng)階段

        idle進(jìn)程的啟動(dòng)是用匯編語(yǔ)言編寫(xiě)(感興趣可以去研究下),對(duì)應(yīng)的啟動(dòng)如下:

s
@bsp\kernel\kernel5.4\arch\arm\kernel\head-common.S
ldmia	r4, {r0, r1, r2, r3}
str	r9, [r0]			@ Save processor ID
str	r7, [r1]			@ Save machine type
str	r8, [r2]			@ Save atags pointer
cmp	r3, #0
strne	r10, [r3]			@ Save control register values
mov	lr, #0
b	start_kernel //啟動(dòng)start_kernel函數(shù)

        其中,語(yǔ)句b start_kernel,b 是跳轉(zhuǎn)的意思,即跳轉(zhuǎn)到start_kernel函數(shù),對(duì)應(yīng)的實(shí)現(xiàn)在bsp/kernel/kernel5.4/init/main.c,至此idle進(jìn)程被啟動(dòng)。

4.3.3 Kernel的C啟動(dòng)階段

        start_kernel()函數(shù)是內(nèi)核初始化C語(yǔ)言部分的主體。這個(gè)函數(shù)完成系統(tǒng)底層基本機(jī)制,包括處理器、存儲(chǔ)管理系統(tǒng)、進(jìn)程管理系統(tǒng)、中斷機(jī)制、定時(shí)機(jī)制等的初始化工作。

c
@bsp\kernel\kernel5.4\init\main.c
asmlinkage __visible void __init start_kernel(void){
    char *command_line;
    char *after_dashes;
    set_task_stack_end_magic(&init_task);
    smp_setup_processor_id();//打印了驅(qū)動(dòng)加載的第一行l(wèi)og
    ... //初始化一系列系統(tǒng)底層機(jī)制
    pr_notice("%s", linux_banner);//打印內(nèi)核版本信息
    ...
    pr_notice("Kernel command line: %s\n", saved_command_line);//打印從uboot傳遞過(guò)來(lái)的command_line字符串
    ...
    /* Do the rest non-__init'ed, we're now alive */
    arch_call_rest_init();//創(chuàng)建init進(jìn)程、kthread進(jìn)程、idle進(jìn)程
    prevent_tail_call_optimization();}

4.3.3.1 kernel啟動(dòng)log

(1)kernel內(nèi)核啟動(dòng)階段,smp_setup_processor_id()函數(shù)會(huì)打印的第一條log(文中l(wèi)og可以在 [目錄5.1開(kāi)機(jī)log] 下載查看),如下:

none
[    0.000000] c0 Booting Linux on physical CPU 0x0

(2)接著會(huì)打印內(nèi)核的一些信息,版本,作者,編譯器版本,日期等,如下:

none
[    0.000000]c0  Linux version 5.4.147+ (lzq@cz-PowerEdge-R730) (Android (7284624, based on r416183b) clang version 12.0.5 (https://android.googlesource.com/toolchain/llvm-project c935d99d7cf2016289302412d708641d52d2f7ee), LLD 12.0.5 (/buildbot/src/android/llvm-toolchain/out/llvm-project/lld c935d99d7cf2016289302412d708641d52d2f7ee)) #5 SMP PREEMPT Thu Sep 28 15:17:40 CST 2023

(3)打印出從uboot傳遞過(guò)來(lái)的command_line字符串,在setup_arch函數(shù)中獲得的(proc/cmdline)

none
[    0.000000]c0  Kernel command line: earlycon=sprd_serial_ex,0x508d0000,115200n8 console=ttySE0,921600n8 loglevel=7 init=/init root=/dev/ram0 rw vmalloc=360M printk.devkmsg=on androidboot.boot_devices=soc/soc:ap-ahb/20600000.sdio initcall_debug=1 swiotlb=1  androidboot.selinux=permissive androidboot.hardware=sl8541e_1h10_32b androidboot.dtbo_idx=0 lcd_id=ID40396 lcd_name=lcd_st7123_truly_mipi_hd lcd_base=9e000000 lcd_size=1440x720 logo_bpix=24 androidboot.ddrsize=1024M androidboot.ddrsize.range=[1024,2048)  sysdump_magic=80001000 sysdump_re_flag=1  androidboot.wdten=0  androidboot.dswdten=disable modem=shutdown ltemode=lcsfb rfboard.id=-1 rfhw.id=0 crystal=2 32k.less=1 androidboot.pmic.chipid=2721 modemboot.method=emmcboot cpcmdline=end  androidboot.verifiedbootstate=orange androidboot.flash.locked=0  androidboot.serialno=LE210210001326000028 buildvariant=userdebug androidboot.vbmeta.device=PARTUUID=1.0 androidboot.vbmeta.avb_version=1.1 androidboot.vbmeta.device_state=unlocked androidboot.

4.3.3.2 init進(jìn)程&kthreadd進(jìn)程

        創(chuàng)建了Linux系統(tǒng)中兩個(gè)重要的進(jìn)程init和kthreadd,并且將當(dāng)前進(jìn)程設(shè)置為idle進(jìn)程:

c
@bsp\kernel\kernel5.4\init\main.cvoid __init __weak arch_call_rest_init(void){
    rest_init();}noinline void __ref rest_init(void){
    ...
    pid = kernel_thread(kernel_init, NULL, CLONE_FS);//創(chuàng)建init進(jìn)程
    ...
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//創(chuàng)建kthreadd進(jìn)程
    ...
    /* Call into cpu_idle with preempt disabled */
    cpu_startup_entry(CPUHP_ONLINE);//設(shè)置當(dāng)前進(jìn)程為idle進(jìn)程}

        Linux下有三個(gè)特殊的進(jìn)程,idle(swapper)進(jìn)程(PID = 0),init進(jìn)程(PID = 1)和看threadd(PID = 2):

(1)idle(swapper)進(jìn)程由系統(tǒng)自動(dòng)創(chuàng)建,運(yùn)行在內(nèi)核態(tài)。idle進(jìn)程其pid=0,其前身是系統(tǒng)創(chuàng)建的第一個(gè)進(jìn)程,也是唯一一個(gè)沒(méi)有通過(guò)fork或者kernel_thread產(chǎn)生的進(jìn)程。完成加載系統(tǒng)后,演變?yōu)檫M(jìn)程調(diào)度、交換,常常被稱為交換進(jìn)程。
(2)init進(jìn)程由idle通過(guò)kernel_thread創(chuàng)建,在內(nèi)核空間完成初始化后,加載init進(jìn)程,并最終轉(zhuǎn)變?yōu)橛脩艨臻g的init進(jìn)程,是系統(tǒng)中所有其他用戶進(jìn)程的祖先進(jìn)程。
(3)kthreadd進(jìn)程是idle通過(guò)kernel_thread創(chuàng)建,并始終運(yùn)行在內(nèi)核空間 ,負(fù)責(zé)所有內(nèi)核線程的調(diào)度和管理。

4.3.3.3 idle進(jìn)程啟動(dòng)

        這個(gè)函數(shù)是Linux內(nèi)核為非引導(dǎo)CPU初始化和進(jìn)入空閑循環(huán)的入口函數(shù),負(fù)責(zé)在系統(tǒng)沒(méi)有任務(wù)需要執(zhí)行時(shí),讓CPU進(jìn)入空閑狀態(tài)。

c
@bsp\kernel\kernel5.4\kernel\sched\idle.cvoid cpu_startup_entry(enum cpuhp_state state){
    arch_cpu_idle_prepare();
    cpuhp_online_idle(state);
    while (1)
        do_idle();}

4.4 Init進(jìn)程

        由上一節(jié)可知,Init進(jìn)程由0號(hào)idle進(jìn)程啟動(dòng),kernel_init()為其入口函數(shù),該函數(shù)主要通過(guò)三種方式啟動(dòng)init程序。

c
static int __ref kernel_init(void *unused){
    ...
    if (ramdisk_execute_command) {
        ret = run_init_process(ramdisk_execute_command);//Step 1. 根據(jù)ramdisk_execute_command的值來(lái)啟動(dòng)init程序
        if (!ret)
            return 0;
        pr_err("Failed to execute %s (error %d)\n",
                ramdisk_execute_command, ret);
    }
    if (execute_command) {
        ret = run_init_process(execute_command);//Step 2. 根據(jù)execute_command的值來(lái)啟動(dòng)init程序
        if (!ret)
            return 0;
        panic("Requested init %s failed (error %d).",
                execute_command, ret);
    }
    if (!try_to_run_init_process("/sbin/init") ||
        !try_to_run_init_process("/etc/init") ||
        !try_to_run_init_process("/bin/init") ||
        !try_to_run_init_process("/bin/sh")) //Step 3.根據(jù)系統(tǒng)默認(rèn)位置來(lái)啟動(dòng)init程序
        return 0;
    panic("No working init found.  Try passing init= option to kernel. "
            "See Linux Documentation/admin-guide/init.rst for guidance.");//Step 4.異常重啟}

(1)ramdisk_execute_command是一個(gè)全局的char指針變量,值為“/init”,也就是根目錄下的init程序。該值通過(guò)uboot傳遞過(guò)來(lái),具體可參考上一節(jié)的command_line。(如未配置該值,即會(huì)將該值默認(rèn)設(shè)置為"/init",該項(xiàng)目是從該處啟動(dòng)init進(jìn)程)
(2)execute_command也是一個(gè)全局的char指針變量,值通過(guò)uboot傳遞;
(3)在前面兩種情況都不滿足的情況下,從系統(tǒng)默認(rèn)位置加載init程序;
(4)如上三種情況都不執(zhí)行的話,進(jìn)入panic函數(shù),則設(shè)備會(huì)異常重啟;
(5)可以通過(guò)如下log確認(rèn)系統(tǒng)啟動(dòng)的是哪個(gè)init程序,該log由run_init_process函數(shù)打印

log
[    1.000614]c1  Run /init as init process

4.4.1 init程序編譯

        我們知道init程序是引用的根目錄下的init,即/init,但是其對(duì)應(yīng)的軟鏈接指向:system/bin/init,相關(guān)信息如下:

編譯makefile文件如下:

none
@system\core\init\Android.bp
phony {
    name: "init",
    required: [
        "init_second_stage",
    ],
}
cc_binary {
    name: "init_second_stage",
    recovery_available: true,
    stem: "init",//最終生成的二進(jìn)制文件名
    defaults: ["init_defaults"],
    static_libs: ["libinit"],
    ...
    srcs: ["main.cpp"],
    ...
}

4.4.2 init程序入口函數(shù)

        init程序的入口函數(shù),根據(jù)不同的入?yún)ⅲ憫?yīng)init不同階段、處理不同業(yè)務(wù)邏輯。

c
@system\core\init\main.cppint main(int argc, char** argv) {
    ...
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);//初始化uevent事件
    }
    if (argc > 1) {
        ...
        if (!strcmp(argv[1], "selinux_setup")) {
            android::mboot::mdb("SELinux Setup ...");
            return SetupSelinux(argv);//selinux權(quán)限
        }
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);//第二階段
        }
    }
    return FirstStageMain(argc, argv);//第一階段}

        由于在kernel階段啟動(dòng)init程序時(shí),未配置參數(shù),故首先會(huì)引用FirstStageMain函數(shù)。另外,根據(jù)log可以看到,其引用順序如下:

none
 [    1.004958]c1  init: init first stage started!
 [    2.547025]c0  init: Opening SELinux policy
 [    3.226672]c0  init: init second stage started!
 [    3.715657]c1  ueventd: ueventd started!
  1. FirstStageMain: 主要?jiǎng)?chuàng)建和掛載基本的文件系統(tǒng),掛載特定分區(qū),啟用log等;

  2. SetupSelinux: 掛載并啟用selinux權(quán)限系統(tǒng);

  3. SecondStageMain: 主要解析ini.rc文件,創(chuàng)建zygote孵化器,fork 進(jìn)程等;

4.4.3 FirstStageMain

        第一階段,該階段所掛載的文件系統(tǒng)都屬于ramdisk,運(yùn)行在虛擬內(nèi)存上。

c
@system\core\init\first_stage_mount.cppint FirstStageMain(int argc, char** argv) {
    ...
    //Step 1. 創(chuàng)建&掛載最基本的文件系統(tǒng)
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));//將/dev設(shè)置為tmpfs并掛載,設(shè)置0755權(quán)限,tmpfs是在內(nèi)存上建立的文件系統(tǒng)(Filesystem)
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mkdir("/dev/dm-user", 0755));
    ...
    //Step 2. 初始化log系統(tǒng)并打印
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);
    ...
    LOG(INFO) << "init first stage started!";
    ...
    //Step 3. 加載內(nèi)核驅(qū)動(dòng)模塊
    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
                           module_count)) {
        ...
    }
    ...
    //Step 4.掛載分區(qū)(system、vendor、product等分區(qū))
    if (!DoFirstStageMount(!created_devices)) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }
    ...
    //Step 5.初始化Android的安全框架Android Verified Boot
    SetInitAvbVersionInRecovery();
    ...
    //Step 6.執(zhí)行下一個(gè)階段
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    ...
    execv(path, const_cast<char**>(args));//exec系列函數(shù)可以把當(dāng)前進(jìn)程替換為一個(gè)新進(jìn)程,且新進(jìn)程與原進(jìn)程有相同的PID,即重新回到main.cpp
    ...}

Step 1. 創(chuàng)建&掛載最基本的文件系統(tǒng),創(chuàng)建了如下五類文件系統(tǒng):

文件系統(tǒng)掛載路徑描述
tmpfs/dev一種虛擬內(nèi)存文件系統(tǒng),它會(huì)將所有的文件存儲(chǔ)在虛擬內(nèi)存中,如果你將tmpfs文件系統(tǒng)卸載后,那么其下的所有的內(nèi)容將不復(fù)存在。tmpfs既可以使用RAM,也可以使用交換分區(qū),會(huì)根據(jù)你的實(shí)際需要而改變大小。
devpts/dev/pts為偽終端提供了一個(gè)標(biāo)準(zhǔn)接口,它的標(biāo)準(zhǔn)掛接點(diǎn)是/dev/pts。只要pty的主復(fù)合設(shè)備/dev/ptmx被打開(kāi),就會(huì)在/dev/pts下動(dòng)態(tài)的創(chuàng)建一個(gè)新的pty設(shè)備文件。
proc/proc一個(gè)虛擬文件系統(tǒng),它可以看作是內(nèi)核內(nèi)部數(shù)據(jù)結(jié)構(gòu)的接口,通過(guò)它我們可以獲得系統(tǒng)的信息,同時(shí)也能夠在運(yùn)行時(shí)修改特定的內(nèi)核參數(shù)。
sysfs/sys與proc文件系統(tǒng)類似,也是一個(gè)不占有任何磁盤(pán)空間的虛擬文件系統(tǒng)。它通常被掛接在/sys目錄下。sysfs文件系統(tǒng)是Linux2.6內(nèi)核引入的,它把連接在系統(tǒng)上的設(shè)備和總線組織成為一個(gè)分級(jí)的文件,使得它們可以在用戶空間存取。
selinuxfs/sys/fs/selinux用于支持SELinux的文件系統(tǒng),SELinux提供了一套規(guī)則來(lái)編寫(xiě)安全策略文件,這套規(guī)則被稱之為 SELinux Policy 語(yǔ)言。

相關(guān)掛載情況如下:

Step 2. 結(jié)合dev/kmsg節(jié)點(diǎn),初始化log系統(tǒng),并在第一階段開(kāi)始時(shí),打印第一條log

none
 [    1.004958]c1  init: init first stage started!

Step 3. 加載內(nèi)核驅(qū)動(dòng)模塊,第一階段加載的內(nèi)核模塊如下:

none
[    1.012653]c1  init: Loaded kernel module /lib/modules/sprd_wdt.ko
[    1.020197]c0  init: Loaded kernel module /lib/modules/sc2721-regulator.ko
[    1.022427]c0  init: Loaded kernel module /lib/modules/nvmem-sc27xx-efuse.ko
[    1.026126]c0  init: Loaded kernel module /lib/modules/spool.ko
[    1.033668]c0  init: Loaded kernel module /lib/modules/sipx.ko
[    1.047553]c0  init: Loaded kernel module /lib/modules/seth.ko
[    1.048526]c0  init: Loaded kernel module /lib/modules/usb_f_vser.ko
...

Step 4. 掛載分區(qū)。創(chuàng)建/first_stage_ramdisk并掛載,然后將根目錄切換到/first_stage_ramdisk,并掛載system、vendor 、product等系統(tǒng)分區(qū),掛載信息如上。
Step 5. 初始化Android的安全框架Android Verified Boot,用于防止系統(tǒng)文件本身被篡改、防止系統(tǒng)回滾,以免回滾系統(tǒng)利用以前的漏洞。
Step 6. 啟動(dòng)下一個(gè)階段SetupSelinux。

4.4.4 SetupSelinux

        該階段主要是初始化Selinux權(quán)限相關(guān)業(yè)務(wù),同時(shí)在業(yè)務(wù)流程最后一步時(shí),重新執(zhí)行system/bin/init程序,再次啟動(dòng)下一個(gè)階段SecondStageMain。

c
@system\core\init\selinux.cppint SetupSelinux(char** argv) {
    ...
    LOG(INFO) << "Opening SELinux policy";
    ...
    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));
    ...
    return 1;}

該階段log打印如下:

none
 [    2.547025]c0  init: Opening SELinux policy

4.4.5 SecondStageMain

        第二階段,涉及到文件系統(tǒng)掛載、屬性服務(wù)等系統(tǒng)相關(guān)業(yè)務(wù),其中最主要的一點(diǎn)去解析rc文件(創(chuàng)建目錄,修改權(quán)限,掛載分區(qū),啟動(dòng)服務(wù)進(jìn)程等),以期讓開(kāi)機(jī)流程進(jìn)入下一階段。

c
@system\core\init\init.cppint SecondStageMain(int argc, char** argv) {
    //Step 1.初始化log
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";
    ...
    //Step 2.屬性服務(wù)初始化,讀取默認(rèn)屬性配置
    PropertyInit();
    ...
    //Step 3.掛載其他文件系統(tǒng),如/apex
    MountExtraFilesystems();
    ...
    //Step 4.啟動(dòng)屬性服務(wù)
    StartPropertyService(&property_fd);
    ...
    //Step 5.加載開(kāi)機(jī)rc文件
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    LoadBootScripts(am, sm);
    ...
    //Step 6.設(shè)置進(jìn)程優(yōu)先級(jí),主進(jìn)程不能退出
    setpriority(PRIO_PROCESS, 0, 0);
    while (true) {
        ...
    }
    return 0;}

Step 1. 初始化log,該階段log打印如下:

none
 [    3.226672]c0  init: init second stage started!

Step 2. 屬性服務(wù)初始化,讀取默認(rèn)屬性配置。獲取system/build.prop、vendor/build.prop、/odm/build.prop、/product/build.prop等其他build.prop屬性,并加載到properties map結(jié)構(gòu)中,然后通過(guò)MMAP映射到全局內(nèi)存中,供所有進(jìn)程調(diào)用;
Step 3. 掛載其他文件系統(tǒng),如/apex。掛載第二階段相關(guān)的文件系統(tǒng);
Step 4. 啟動(dòng)屬性服務(wù)。創(chuàng)建socket,處理客戶端發(fā)來(lái)的請(qǐng)求,決定是更新屬性值還是新增屬性值;
Step 5. 加載開(kāi)機(jī)rc文件,rc文件的加載順序如下;

none
[    3.481952]c0  init: Parsing file /system/etc/init/hw/init.rc...
[    3.489693]c0  init: Parsing file /init.environ.rc...
[    3.490110]c0  init: Parsing file /system/etc/init/hw/init.usb.rc...
[    3.491867]c0  init: Parsing file /init.sl8541e_1h10_32b.rc...
[    3.493283]c0  init: Parsing file /vendor/etc/init/hw/init.sl8541e_1h10_32b.rc...
[    3.494372]c0  init: Parsing file /vendor/etc/init/hw/init.sl8541e_1h10_32b.usb.rc...
[    3.509664]c0  init: Parsing file /vendor/etc/init/hw/init.ram.rc...
...

解析init.rc會(huì)把一條條命令映射到內(nèi)存中,然后依次啟動(dòng),啟動(dòng)順序如下:

none
on on early-init:在初始化早期階段觸發(fā)
on init:在初始化階段觸發(fā)
on late-init:在初始化晚期階段觸發(fā)
on boot/charger:當(dāng)系統(tǒng)啟動(dòng)/充電時(shí)觸發(fā)
on property:當(dāng)屬性值滿足條件時(shí)觸發(fā)

Step 6. 設(shè)置進(jìn)程優(yōu)先級(jí),主進(jìn)程不能銷(xiāo)毀和退出。

4.5 Zygote進(jìn)程

4.5.1 Zygote進(jìn)程啟動(dòng)腳本

(1)rc文件編譯
        init.rc文件是一個(gè)配置文件,最終會(huì)被打包到該目錄:/system/etc/init/hw/

c
@system\core\rootdir\Android.bp
prebuilt_etc {
    name: "init.rc",
    src: "init.rc",
    sub_dir: "init/hw",
    required: [
        "fsverity_init",
        "platform-bootclasspath",
    ],}

(2)init.rc
        由上一節(jié)我們知道,當(dāng)init程序啟動(dòng)到第二階段時(shí)候,會(huì)去加載rc文件,且最開(kāi)始加載的rc文件是:system/etc/init/hw/init.rc

c
@system\core\rootdir\init.rc...import /system/etc/init/hw/init.${ro.zygote}.rc//導(dǎo)入zygote的rc文件,ro.zygote屬性可根據(jù)系統(tǒng)讀取...# Mount filesystems and start core system services.on late-init    ...
    # Now we can start zygote for devices with file based encryption
    trigger zygote-start //在開(kāi)機(jī)初始化晚期階段,觸發(fā)zygote啟動(dòng)
    ...# It is recommended to put unnecessary data/ initialization from post-fs-data# to start-zygote in device's init.rc to unblock zygote start.on zygote-start && property:ro.crypto.state=unencrypted
    wait_for_prop odsign.verification.done 1
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start statsd
    start netd
    start zygote
    start zygote_secondary

(3)init.zygote32.rc
        啟動(dòng) app_process,并改名為zygote,使用:-Xzygote、/system/bin、–zygote和–start-system-server等參數(shù),同時(shí)重啟了audioserver、cameraserver、media等服務(wù)。

r
@system\core\rootdir\init.zygote32.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks
    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

(4)zygote啟動(dòng)log
        在解析完rc文件后,會(huì)啟動(dòng)對(duì)應(yīng)的服務(wù),zygote啟動(dòng)log如下:

none
[    7.916749]c3  init: starting service 'zygote'...

4.5.2 Zygote進(jìn)程

(1)zygote編譯文件

none
@frameworks\base\cmds\app_process\Android.bp
cc_binary {
    name: "app_process",
    srcs: ["app_main.cpp"], 
    ...
}

(2)app_process啟動(dòng)
        zygote是一個(gè)名為zygote的app_process進(jìn)程,解析rc文件參數(shù),啟動(dòng)ZygoteInit。

c
int main(int argc, char* const argv[]){
    ...
    while (i < argc) {//讀取rc文件傳進(jìn)來(lái)的參數(shù)
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } 
        ...
    }
    ...
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);//啟動(dòng)ZygoteInit
    } 
    ...}

(3)AndroidRuntime
        通過(guò)反射去啟動(dòng)ZygoteInit的main函數(shù),相關(guān)引用如下:

c
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote){
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());
    ...
    //通過(guò)反射啟動(dòng)函數(shù)
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);#endif
        }
    }
    ...}

當(dāng)前流程會(huì)打印如下log:

none
01-01 08:00:08.538   387   387 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<<

4.5.3 Zygote進(jìn)程(java)

(1)ZygoteInit

java
@frameworks\base\core\java\com\android\internal\os\ZygoteInit.javapublic static void main(String[] argv) {
    ...
    try {
        //Step 1. 預(yù)加載資源文件
        if (!enableLazyPreload) {
            bootTimingsTraceLog.traceBegin("ZygotePreload");
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
            preload(bootTimingsTraceLog);
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
            bootTimingsTraceLog.traceEnd(); // ZygotePreload
        }
        ...
        //Step 2.注冊(cè)Zygote的socket監(jiān)聽(tīng)接口
        zygoteServer = new ZygoteServer(isPrimaryZygote);
        //Step 3.創(chuàng)建system_server進(jìn)程
        if (startSystemServer) {
            Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
            if (r != null) {
                r.run();
                return;
            }
        }
        //Step 4.主線程loop消息循環(huán)
        caller = zygoteServer.runSelectLoop(abiList);
    } 
    ...}

Step 1. 提前加載類,加載系統(tǒng)資源(如一些公共的庫(kù)、SDK等),這樣當(dāng)程序被fork處理后,應(yīng)用的進(jìn)程內(nèi)已經(jīng)包含了這些系統(tǒng)資源,大大節(jié)省了應(yīng)用的啟動(dòng)時(shí)間。
Step 2. 注冊(cè)Zygote的socket監(jiān)聽(tīng)接口,用來(lái)接收啟動(dòng)應(yīng)用程序的消息;
Step 3. frok出SystemServer進(jìn)程;
Step 4. 主線程loop消息循環(huán)

(2)forkSystemServer
        fork出SystemServer進(jìn)程,并且去調(diào)用SystemServer進(jìn)程的main函數(shù)

java
@frameworks\base\core\java\com\android\internal\os\ZygoteInit.javaprivate static Runnable forkSystemServer(String abiList, String socketName,
        ZygoteServer zygoteServer) {
    ...
    /* Hardcoded command line to start the system server */
    String[] args = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
                    + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
            "com.android.server.SystemServer",
    };//啟動(dòng)SystemServer相關(guān)參數(shù)
    ...
    try {
        ...
        /* Request to fork the system server process */
        pid = Zygote.forkSystemServer(
                parsedArgs.mUid, parsedArgs.mGid,
                parsedArgs.mGids,
                parsedArgs.mRuntimeFlags,
                null,
                parsedArgs.mPermittedCapabilities,
                parsedArgs.mEffectiveCapabilities);//fork出SystemServer進(jìn)程
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException(ex);
    }
    /* For child process */
    if (pid == 0) {
        ...
        return handleSystemServerProcess(parsedArgs);//通過(guò)反射調(diào)用SystemServer進(jìn)程的main函數(shù)
    }
    return null;}

(3)handleSystemServerProcess
        此函數(shù)為調(diào)用SystemServer進(jìn)程,最終會(huì)通過(guò)反射調(diào)用SystemServer進(jìn)程的main函數(shù),其方法調(diào)用棧如下:
handleSystemServerProcess()->ZygoteInit.zygoteInit()->RuntimeInit.applicationInit()->RuntimeInit.findStaticMain()

java
@frameworks\base\core\java\com\android\internal\os\RuntimeInit.javaprotected static Runnable findStaticMain(String className, String[] argv,
        ClassLoader classLoader) {
    Class<?> cl;
    try {
        cl = Class.forName(className, true, classLoader);//反射調(diào)用類:com.android.server.SystemServer
    }
    ...
    Method m;
    try {
        m = cl.getMethod("main", new Class[] { String[].class });//反射調(diào)用方法:main
    } 
    ...
    return new MethodAndArgsCaller(m, argv);}

4.6 SystemServer進(jìn)程

        Android系統(tǒng)在啟動(dòng)的時(shí)候,在啟動(dòng)兩個(gè)重要的進(jìn)程,一個(gè)是Zygote進(jìn)程,另一個(gè)是由zygote進(jìn)程fork出來(lái)的system_server進(jìn)程。SystemSever負(fù)責(zé)啟動(dòng)系統(tǒng)的各項(xiàng)服務(wù),Android系統(tǒng)中Java世界的核心Service都在這里啟動(dòng)。

4.6.1 main函數(shù)

由上一章節(jié)我們知道,SystemServer的入口函數(shù)是main方法,如下:

java
@frameworks\base\services\java\com\android\server\SystemServer.javapublic static void main(String[] args) {
    new SystemServer().run();}private void run() {
    ...    
    Slog.i(TAG, "Entered the Android system server!");
    ...
    // Start services.
    try {
        t.traceBegin("StartServices");
        startBootstrapServices(t);//引導(dǎo)服務(wù)
        startCoreServices(t);//核心服務(wù)
        startOtherServices(t);//其他服務(wù)
    } 
    ...
    // Loop forever.
    Looper.loop();//主線程循環(huán)隊(duì)列
    throw new RuntimeException("Main thread loop unexpectedly exited");}

SystemServer進(jìn)程成功啟動(dòng),會(huì)打印如下log:

none
01-01 08:00:21.430  1001  1001 I SystemServer: Entered the Android system server!

4.6.2 SystemServer中啟動(dòng)服務(wù)

        在一系列的java服務(wù)中,可以分為三類:系統(tǒng)boot級(jí)別服務(wù)、核心服務(wù)、其他服務(wù),其對(duì)應(yīng)如下:

類型服務(wù)備注
startBootstrapServicesActivityManagerService簡(jiǎn)稱AMS,管理Android四大組件的生命周期
PowerManagerService電源管理服務(wù)
PackageManagerService簡(jiǎn)稱PMS,用于APK的安裝、卸載、權(quán)限驗(yàn)證等
UserManagerService用戶創(chuàng)建、刪除、查詢等
......
startCoreServicesBatteryService對(duì)設(shè)備電池狀態(tài)進(jìn)行監(jiān)控
UsageStatsService收集App的使用頻率等信息
......
startOtherServicesCameraService管理設(shè)備相機(jī)功能
BluetoothService管理藍(lán)牙服務(wù)
WindowManagerService簡(jiǎn)稱WMS,管理窗口服務(wù)
FingerPrintService管理指紋服務(wù)
......

4.7 Home進(jìn)程

        一般情況下,Android原生的軟體會(huì)包含兩個(gè)home進(jìn)程,一個(gè)是Settings進(jìn)程的Fallbackhome,一個(gè)是Launcher進(jìn)程。

4.7.1 Home進(jìn)程的編譯

(1)FallbackHome編譯
        Fallbackhome位于Settings進(jìn)程內(nèi),隨系統(tǒng)啟動(dòng)時(shí)被拉起。

none
@packages\apps\Settings\Android.bp
android_app {
    name: "Settings",
    defaults: ["platform_app_defaults"],
    platform_apis: true,
    certificate: "platform",
    system_ext_specific: true,
    privileged: true,
    ...
}

(2)Launcher進(jìn)程編譯
        Launcher進(jìn)程主要用于顯示App的界面信息。隨著Android版本的迭代、以及層出不窮的產(chǎn)品,目前Android的Launcher版本較多,如Home、Launcher2、Launcher3、Launcher3QuickStep,要根據(jù)自身項(xiàng)目的配置,找到對(duì)應(yīng)的Launcher應(yīng)用。

none
@packages\apps\Launcher3\Android.mk
include $(CLEAR_VARS)
...
LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
LOCAL_PRIVILEGED_MODULE := true
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
...

4.7.2 Launcher進(jìn)程啟動(dòng)

        FallbackHome是系統(tǒng)由未解密到解密過(guò)程的一個(gè)過(guò)度界面,只要用戶把系統(tǒng)解鎖過(guò)一次后,F(xiàn)allbackHome收到解鎖廣播就會(huì)退出,而WMS檢測(cè)到當(dāng)前Acitivity棧是空的,進(jìn)而啟動(dòng)真正的Launcher。由于FallbackHome沒(méi)有界面,所以可能會(huì)出現(xiàn)一個(gè)問(wèn)題,home進(jìn)程切換時(shí)會(huì)出現(xiàn)空白界面,接下來(lái)才是顯示Launcher的一個(gè)圖標(biāo)界面。

java
@packages\apps\Settings\src\com\android\settings\FallbackHome.javaprotected void onCreate(Bundle savedInstanceState) {
    ...
    registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));//注冊(cè)ACTION_USER_UNLOCKED廣播
    maybeFinish();}private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        maybeFinish();//接收ACTION_USER_UNLOCKED廣播
    }};private void maybeFinish() {
    if (getSystemService(UserManager.class).isUserUnlocked()) {
        final Intent homeIntent = new Intent(Intent.ACTION_MAIN)
                .addCategory(Intent.CATEGORY_HOME);
        final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0);//查詢home包名信息,此處一般是返回Launcher的信息
        if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) {
            Log.d(TAG, "User unlocked but no home; let's hope someone enables one soon?");
            mHandler.sendEmptyMessageDelayed(0, 500);//間隔500ms輪詢
        } else {
            Log.d(TAG, "User unlocked and real home found; let's go!");
            getSystemService(PowerManager.class).userActivity(
                    SystemClock.uptimeMillis(), false);
            finish();//結(jié)束當(dāng)前Activity,啟動(dòng)Launcher應(yīng)用
        }
    }}

4.7.3 FallbackHome進(jìn)程啟動(dòng)

(1) 啟動(dòng)home進(jìn)程
        剛開(kāi)機(jī)時(shí),SystemSever進(jìn)程會(huì)啟動(dòng)WMS服務(wù),如果WMS未檢測(cè)到Activity棧有任務(wù)時(shí),會(huì)啟動(dòng)一個(gè)默認(rèn)的home進(jìn)程,此進(jìn)程即FallbackHome。

java
@frameworks\base\services\core\java\com\android\server\wm\RootWindowContainer.javavoid startHomeOnEmptyDisplays(String reason) {
    forAllTaskDisplayAreas(taskDisplayArea -> {
        if (taskDisplayArea.topRunningActivity() == null) {
            startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea,
                    false /* allowInstrumenting */, false /* fromHomeKey */);
        }
    });}

(2) ACTION_USER_UNLOCKED廣播發(fā)送
        系統(tǒng)解鎖時(shí)會(huì)發(fā)送該廣播,相關(guān)代碼如下:

java
@frameworks\base\services\core\java\com\android\server\am\UserController.javavoid finishUserUnlocked(final UserState uss) {
    ...
    if (!mInjector.getUserManager().isPreCreated(userId)) {
        // Dispatch unlocked to external apps
        final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
        unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
        unlockedIntent.addFlags(
                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
        mInjector.broadcastIntent(unlockedIntent, null, null, 0, null,
                null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
                Binder.getCallingUid(), Binder.getCallingPid(), userId);
    }
    ...}

五、相關(guān)資源

5.1 開(kāi)機(jī)log

串口+logcat:https://download.csdn.net/download/u013320490/88800008

六、小結(jié)

        本文章涉獵Android多個(gè)層級(jí),旨在梳理整體流程,對(duì)Android設(shè)備的啟動(dòng)有一個(gè)感性的認(rèn)識(shí),能夠達(dá)到一定邏輯自洽。
        Android的每個(gè)模塊、每個(gè)進(jìn)程、每行代碼都有其深度,沒(méi)有細(xì)細(xì)揣測(cè)與推敲,是有點(diǎn)"褻瀆"了,他日必對(duì)其中感興趣模塊加以研究,respect!

七、參考資料

  1. Android系統(tǒng)架構(gòu)圖
    https://developer.android.google.cn/guide/platform?hl=zh-cn

  2. Android Hal層的由來(lái)
    https://blog.csdn.net/Xiaoma_Pedro/article/details/130253665

  3. 開(kāi)機(jī)流程
    https://www.ancii.com/acglj4qvw/
    https://gityuan.com/android/#二android架構(gòu)

  4. Kernel內(nèi)核
    https://blog.51cto.com/u_16213627/8681479
    https://blog.csdn.net/CAUC_learner/article/details/120435753
    https://blog.csdn.net/daringtodoit/article/details/24675867
    https://blog.csdn.net/marshal_zsx/article/details/80225854
    https://blog.51cto.com/u_11947739/6360569
    https://blog.csdn.net/qq_38499859/article/details/88187602

  5. init進(jìn)程分析
    https://www.yii666.com/blog/693472.html
    https://blog.csdn.net/hai_qing_xu_kong/article/details/85707697
    https://www.yii666.com/blog/423299.html

  6. RC文件
    https://blog.csdn.net/zxc024000/article/details/111473100

作者:林奮斗同學(xué),轉(zhuǎn)自博客園 https://www.cnblogs.com/zhiqinlin/p/18001113


該文章在 2024/2/1 16:39:13 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車(chē)隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷(xiāo)售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved