MTK系统启动流程
boot rom -> preloader ->lk ->kernel ->Native -> Android
1、Boot rom:系统开机,最先执行的是固化在芯片内部的bootrom,其作用主要有
a.初始化ISRAM和EMMC
b.当系统全擦后 ,也会配置USB,用来仿真USB端口下载镜像。
c.从EMMC中加载preloader到ISRAM中执行。
2、Preloader:主要完成平台基础设备的初始化,如DDR等硬件,下载握手连接,加载lk到DDR中,跳转到lk
3、Lk:打开MMU,加速lk执行,显示log、充电相关,从emmc中boot分区取出boot.img进行解压,将根文件系统,linux kernel加载到Dram,完成kernel前的准备工作并拉起kernel(start_kernel)。
4、Kernel:初始化调度、内存管理、io、中断等linux操作系统基本功能、识别OTBO,创建init进程,完成模块的初始化,执行init主程序。
5、Native:启动系统关键进程init、void、surfaceflinger等
init 进程的作用大致分为3部分:
1、创建备结点,挂载跟文件系统,
2、开启selinux安全策略,初始化资源文件,启动属性服务等;
3、解析initrc 文件;
6、Android:init fork zygote进程,再由zygote去创建所有安卓的应用进程
基本流程图如下:
属性查看是否第一次开机
persist.sys.device_provisioned开机向导是否完成
persist.sys.device_first_boot是否第一次开机
一、preloader(汇编/C)
1.1 作用
1、完成平台基础设备的初始,如DDR等硬件
2、与flashtoolUSB握手,download 相关检测 & sec boot检测;
3、将lk加载到DRAM
4、跳转到Ik
1.2 入口代码
/MTXXXX_13_Master/vnd/vendor/mediatek/proprietary/bootable/bootloader/preloader/platform/mtXXXX/src/init/init.s
init.s:初始化寄存器、清空bss段、初始化stack、设置CPSR、切换到SVC模式、禁止中断;完成以上基本初始化后jump to main
setup_stk : /* setup stack */ LDR r0, stack LDR r1, stacksz /* buffer overflow detect pattern */ LDR r2, =0xDEADBEFF STR r2, [r0] LDR r1, [r1] SUB r1, r1, #0x04 ADD r1, r0, r1 MOV sp, r1 entry : LDR r0, =bldr_args_addr B main .globl jump
/MTXXXX_13_Master/vnd/vendor/mediatek/proprietary/bootable/bootloader/preloader/platform/mtXXXX/src/core/main.c
static void bldr_pre_process(void) { int isLocked = 0; int tempnoboot = 0; uint32_t ret = 0; ...... //初始化timer/pll/gpio/uart/WDT/storage/PMIC platform_pre_init(); pal_log_info("\n%s Build Time: %s\n", MOD, BUILD_TIME); ...... /* hardware initialization */ //初始化RTC/PM/batter/DDR/storage platform_init(); tempnoboot = oplus_bq28z610_temp(); no_boot_log_to_storage(tempnoboot); ...... } void main(u32 *arg) { struct bldr_command_handler handler; u32 jump_addr, jump_arg; ...... //初始化平台环境 bldr_pre_process(); ....... BOOTING_TIME_PROFILING_LOG("before bldr_handshake"); //与下载工具握手连接 bldr_handshake(&handler); BOOTING_TIME_PROFILING_LOG("bldr_handshake"); //初始化安全环境 trustzone_pre_init(); BOOTING_TIME_PROFILING_LOG("trustzone pre init"); #endif #if !(CFG_BYPASS_LOAD_IMG_FORCE_ATF) /* Do not load ATF, lk, load by JTAG */ //加载LK镜像到DRAM if (0 != bldr_load_images(&jump_addr)) { pal_log_err("%s Second Bootloader Load Failed\n", MOD); #if !CFG_BYPASS_EMI goto error; #endif //SLT //完成preloader剩余的初始化,主要对电池的初始化 bldr_post_process(); ....... //跳转到lk bldr_jump64(jump_addr, jump_arg, sizeof(boot_arg_t)); ....... }
bldr_pre_process 初始化平台环境
platform_pre_init:初始化timer/pll/gpio/uart/WDT/storage/PMIC
platform_init:初始化RTC/PM/batter/DDR/storage
bldr_handshake 与下载工具握手连接
trustzone_pre_init 安全环境初始化
bldr_load_images 加载LK镜像到DRAM
bldr_post_process 完成preloader剩余的初始化,主要对电池的初始化
bldr_jump64跳转到lk
二、LK(汇编/C)
2.1 作用
1、打开MMU,使能I/D-cache,加速lk执行,显示logo、充电相关
·2、从emmc中boot分区取出bootimg解压,将根文件系统(ramdisk)、linux kernel load到DRAM:
3、解析dtb,写入到DRAM指定区域:
4、关闭MMU、irg /fg,关闭ID-cache,拉起 kernel(startkemel);
2.2 入口代码:
/MTXXXX_13_Master/vnd/vendor/mediatek/proprietary/bootable/bootloader/lk/arch/arm/crt0.S
crt0.s:添加TZ symbol,执行abort stack等初始化操作。跳转到kmain
2.3 kmain
/MTXXXX_13_Master/vnd/vendor/mediatek/proprietary/bootable/bootloader/lk/kernel/main.c
void kmain(void) { //初始化工作队列、线程表、创建bootstrap线程开始后面初始化 thread_init_early(); // 初始化MMU、cache arch_early_init(); //平台初始化。如timmer、gpio、pmic等,建立lk基本运行环境 platform_early_init(); ....... #if (!ENABLE_NANDWRITE) // create a thread to complete system initialization dprintf(SPEW, "creating bootstrap completion thread\n"); //thread_bs2:创建bootstrap2线程,用于完成后续的初始化并加载app bootstrap2 thread_t *thread_bs2 = thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE); ...... }
1. 初始化系统任务所需的线程、链表和相关队列。
2. 初始化硬件平台,搭建lk基本运行环境
3. 初始化定时器等
4. 创建一个新线程bootstrap2,并执行。当前线程置于空闲状态
2.4 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 // Allocate LK memory from mb, free before jump to kernel mboot_allocate_lk_scratch_from_mblock(); // 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; }
bootstrap2线程主函数:初始化bio、fs、platform、app
app_init 逐个app初始化并运行
mt_boot执行后续启动流程,最终加载kernel的app
三、Kernel(汇编/C)
3.1 作用
初始化调度、内存管理、io、中断等linux操作系统基本功能、识别DTBO、创建init进程、完成模块的初始化,执行init主程序
3.2 入口代码
/MTXXXX_13_Master/vnd/kernel/kernel-5.10/arch/arm64/kernel/head.S
MMU、CPU、页表、Cache等初始化,跳转到start_kernel
3.3 start_kernel
/MTXXXX_13_Master/vnd/kernel/kernel-5.10/init/main.c
在一系列基本初始化完成后,rest_init->kernel_init 创建init进程,并在/sbin/etc/bin依次寻找init可执行文件
3.4 DTBO识别流程
start_kernel->setup_arch->unflatten_device_tree->unflatten_device_tree->unflatten_dt_nodes
在unflatten_dt_nodes中遍历每个节点,对每个节点调用populate_node,将每个节点的信息转化为device_node结构;
unflatten_dt_alloc创建device_node结构
populate_propertites创建property结构,并将device_node属性添加进去
3.5 platform bus初始化流程
do_basic_setup->driver_init
devices_init:创建/sys/devices /sys/dev两个设备根节点,创建/sys/dev/block /sys/dev/char
buses_init:创建/sys/bus bus根节点
platform_bus_init
注册platform_bus设备,创建/sys/devices/plaform节点,此节点是所有platform设备的根节点
注册platform_bus_type总线类型,创建/sys/bus/platfrom节点,此节点是所有platform设备的总线类型根节点
3.6 将设备树种的设备加入platform devices:
入口函数
/MTXXXX_13_Master/vnd/kernel-5.10/drivers/of/platform.c
通过arch_initcall_sync将此函数加入kernel初始化函数集
通过of_platform_default_populate->of_plattform_populate->platform_bus_create此调用流程挑选合适的设备加入platform bus
最后通过of_plaform_device_create_pdata->of_device_add将设备添加到platform devices
四、Native(C/C++)
4.1 作用
启动系统关键进程init、void、surfaceflinger
4.2 init入口代码
/MTXXXX_13_Master/sys/system/core/init/main.cpp
int main(int argc, char** argv) { ...... if (argc > 1) { if (!strcmp(argv[1], "subcontext")) { android::base::InitLogging(argv, &android::base::KernelLogger); const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); return SubcontextMain(argc, argv, &function_map); } if (!strcmp(argv[1], "selinux_setup")) { return SetupSelinux(argv); } if (!strcmp(argv[1], "second_stage")) { return SecondStageMain(argc, argv); } } //init第一阶段 return FirstStageMain(argc, argv); }
4.3 init第一阶段 FirstStageMain
/MTXXXX_13_Master/sys/system/core/init/first_stage_main.cpp
1、创建系统目录、挂载对应分区
2、初始化init阶段log到kernel log
3、加载SElinux安全策略
4.4 init第二阶段 SecondStageMain
/MTXXXX_13_Master/sys/system/core/init/init.cpp
1、初始化属性系统,从指定位置读取属性设置:-->Propertyinit()
2、挂载apex/分区-->MountExtraFilesystem s ()
3、SElinux第二阶段-->SelinuxRestoreContext() :
4、启动属性服务,以便增加、修改、获取属性-->StartPropertyService ()
5、初始化文件上下文-->InitializeSubcontext()
6、将am、sm添加到集合中,以便后续按顺序执行-->LoadBootscripts (am , sm )
7、执行init.rc
4.5 initrc语法:安卓启动脚本专用语法
四要素:Action、Command、Service、Option
4.5.1 Action
由一组命令和一个触发器组成,触发器语句以on开头,on后的条件决定执行的时机,当一些条件满足触发器的条件时,该Action中定义的命令会被添加到要执行命令队列的尾部(如果这组命令已经在队列中,则不会再次添加)。
常用的triggers如下:
- early-init 在初始化早期阶段触发
- late-init 在初始化晚期阶段触发
- init 在初始化阶段触发
- late-init 在初始化晚期阶段触发
- boot/charger 当系统启动/充电时触发
- property:<key>=<value> 当属性值满足条件时触发 如:on property:ro.debuggable=1
- fs 挂载mtd分区时触发
- boot 基本网络的初始化,内存管理等时触发
- post-fs 改变系统目录的访问权限时触发
- device-added-<path> 设备节点添加时触发
- device-removed-<path> 设备节点删除时触发
- service-exited-<name> 在特定服务(service)退出时触发
on earty-init创建文件目录,给进程设置限制(nice、fd)启动 ueventd
on init创建文件目录,给各节点分用户组,启动logd、Imkd、servicemanager等服务
on late-init
on early-fs:启动voId
on fs:挂载文件目录
on postfs:挂载文件系统 rootfs(remount)。
on late-fs:存储加密解锁前所需的 HALs如(keyasten)
on post-fs-data:挂载data,启动apexd,初始化第一个用户,启动fuse,
zygote-start:需和加密状态一起,启动zygote.netdzygote-secondnary
on boot:启动 hal,启动core service
4.5.2 command
常用命令:
1).import <filename> 导入init.XX.rc、xxx.conf等文件 Parse an init config file, extending the current configuration. 2).chmod <octal-mode> <path> Change file access permissions. 3).chown <owner> <group> <path> Change file owner and group. 4).chdir <directory> Change working directory. 5).chroot <directory> 改变进程根目录 6).insmod <path> 加载XX.ko驱动模块 7).start <service> Start a service running if it is not already running. 8).stop <service> Stop a service from running if it is currently running. 9).class_start <serviceclass> Start all services of the specified class if they are not already running. 10).class_stop <serviceclass> Stop all services of the specified class if they are currently running. class_reset <serviceclass> //重启class下面所有的服务 11).setprop <name> <value> Set system property <name> to <value>. 通过getprop命令可以查看当前系统的属性值 12).export <name> <value> 设置全局环境变量,这个变量值可以被所有进程访问(全局的,一直存在) 在代码中通过value = getenv("name")接口可以获取这个环境变量的值 13).mkdir <path> [mode] [owner] [group] 创建目录,后面项缺省值为 mode,owner,group: 0755 root root 14).trigger <event> Trigger an action. Used to queue an action from another action. 例:trigger post-fs-data 15).exec <path> [ <argument> ]* 执行<path>指定的Program,并可以带有执行参数。 exec在调用进程内部执行一个可执行文件,并会阻塞当前进程,直到运行完成。 最好避免和那些builtin commands一样使用exec命令,否则容易造成阻塞 or stuck ( maybe there should be a timeout?) 16).ifup <interface> 启动某个网络接口,使其为up状态,通过netcfg可以查看,ifup eth0 等价于 netcfg eth0 up 功能一样 17).hostname <name> 设置设备的主机名,一般默认设置为localhost,可以在终端通过hostname new_name进行修改 18).domainname <name> 设置网络域名localdomain 19).mount <type> <device> <dir> [ <mountoption> ]* 把device挂接到dir目录下面,文件系统类型为type。 <mountoption>s include "ro", "rw", "remount", "noatime", “nosuid”......,具体可查看[linux](http://lib.csdn.net/base/linux "Linux知识库")的mount命令说明 20).setkey TBD == to be determined 暂时没有使用 21).setrlimit <resource> <cur> <max> 设置本服务进程的资源上限值。(使用例子??) 22).symlink <target> <path> path 链接到 ---》target ;创建符号链接 23).sysclktz <mins_west_of_gmt> 设置系统时区(0 if system clock ticks in GMT) 24).wait <path> [ <timeout> ] 轮询查找给定的文件path是否存在,如果找到或者超时则返回默认超时为5秒。(使用实例???) 25).write <path> <string> [ <string> ]* 打开一个文件,利用write命令写入一个或多个字符串
4.5.3 service
Services(服务)是一个程序,以 service开头,由init进程启动,一般运行于另外一个init的子进程,所以启动service前需要判断对应的可执行文件是否存在。init生成的子进程,定义在rc文件,其中每一个service,在启动时会通过fork方式生成子进程。Services(服务)的形式如下:
服务(services)是指那些须要在系统初始化时就启动或退出时自己主动重新启动的程序.
它的语法结构例如以下:
1. service <name> <pathname> [ <argument> ]* 2. <option> 3. <option> 4. ...
pathname: 必须要有可执行的权限 service的name: 在所有rc文件中不能重复
4.5.4 option 选项
- class <class_name>
说明服务属于class_name这个类。缺省值service属于 “default” 类。同一个class下面的服务可以一起启动或停止。 - disabled
表示当这个服务所在的class启动的时候,服务不会自动启动,
要用start server_name 或 property_set("ctl.start", server_name);才能启动。 - oneshot
当服务退出后,不会再重新启动,如果没有加这个option,则服务默认退出后又会重新重启 - user <username>
执行服务之前,先声明服务的用户名,缺省值应该为root用户. - group <groupname> [ <groupname> ]*
执行服务之前,先声明服务所属组名,可以一次声明属于多个组。
声明多个组时,除第一个组名外,其他的为服务的补充组名(调用接口 setgroups()). - onrestart + command
服务重启的时,会执行onrestart后面的command.
eg:onrestart restart media 重启名为media的服务 - setenv <name> <value>
在当前服务进程中设置环境变量name的值为value。
注意:setenv定义的环境变量仅在本进程内生效,退出该进程,或者关闭相应的程序运行窗口,该环境变量即无效)
程序中可通过getenv("name")接口获取这个环境变量的值
setenv和export 的区别:
setenv csh ,本进程生效,退出后,变量无效
export bash ,全局生效,一直存在
格式:
export key=value
setenv key value - critical
声明为关键服务。如果服务在四分钟内退出了四次,则设备会进入recovery模式 - socket <name> <type> <perm> [ <user> [ <group> ] ]
创建名为/dev/socket/<name>的unix domain socket ,并把它的句柄fd传给本服务进程
<type> 必须为 "dgram", "stream" or "seqpacket".User and group default to 0 ,也就是root. - seclablel 执行服务之前改变安全上下文
五、Android(JAVA)
init fork zygote进程,再由zygote去创建所有安卓的应用进程。
5.1 ZygoteInit
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) { //1、创建ZygoteServer ZygoteServer zygoteServer = new ZygoteServer(); ....... try { ....... boolean startSystemServer = false; String socketName = "zygote"; String abiList = null; boolean enableLazyPreload = false; // 2、解析app_main.cpp传来的参数 for (int i = 1; i < argv.length; i++) { if ("start-system-server".equals(argv[i])) { startSystemServer = true; } else if ("--enable-lazy-preload".equals(argv[i])) { enableLazyPreload = true; } else if (argv[i].startsWith(ABI_LIST_ARG)) { abiList = argv[i].substring(ABI_LIST_ARG.length()); } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { socketName = argv[i].substring(SOCKET_NAME_ARG.length()); } else { throw new RuntimeException("Unknown command line argument: " + argv[I]); } } if (abiList == null) { throw new RuntimeException("No ABI list supplied."); } //3、创建一个Server端的Socket zygoteServer.registerServerSocket(socketName); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. if (!enableLazyPreload) { bootTimingsTraceLog.traceBegin("ZygotePreload"); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); //4、加载进程的资源和类 preload(bootTimingsTraceLog); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); bootTimingsTraceLog.traceEnd(); // ZygotePreload } else { Zygote.resetNicePriority(); } ........ if (startSystemServer) { //5、开启SystemServer进程 startSystemServer(abiList, socketName, zygoteServer); } Log.i(TAG, "Accepting command socket connections"); //6、启动一个死循环监听来自Client端的消息 zygoteServer.runSelectLoop(abiList); //7、关闭SystemServer的Socket zygoteServer.closeServerSocket(); } catch (Zygote.MethodAndArgsCaller caller) { //8、这里捕获这个异常调用MethodAndArgsCaller的run方法。 caller.run(); } catch (Throwable ex) { Log.e(TAG, "System zygote died with exception", ex); zygoteServer.closeServerSocket(); throw ex; } }
5.2 Zygote流程
1、创建java虚拟机
2、为java虚拟机注册native方法
3、在com.android.initernal.os.ZygoteInit中调用java类的主要方法
4、加载Zygotelnit class
5、注册Zygote socker
6、加载preload class
7、加载preload资源文件
8、调用Zygote::forkSystemServer、fork一个新的进程,调用SystemSercer的main方法,启动SystemServer进程
1.解析init.zygote.rc中的参数,创建AppRuntime并调用AppRuntime.start()万法
2.调用AndroidRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数
3.通过JNI方式调用Zygotelnitmain0),第一次进入Java世界
4.registerZygoteSocket()建立socket通道,zygote作为通信的服务端用于响应客户端请求
5.preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率
6.通过startSystemServer(),fork得力帮手system_server进程
7.调用runSelectLoop0(),当接收到请求创建新进程请求时立即唤醒并执行相应工作
5.3 Zygote作用
创建SystemServer
孵化应用进程
5.3 相关进程
init 进程:当内梭创建了 init 进程之后便去执行 init,init 进程的最后阶段对 init,rc脚本进行解析,启动各种进程服务,其中最重要的是启动了 zygote 进程。
zygote 进程: zygote 进程承上启下,是系统开启的第一个 java 进程,运行在 framework层;该进程主要负责创建包括 systemserver 在内的所有应用程序,以及创建虚拟机运行环境。
systemserver 进程: 启动 Binder 线程池和 SystemServiceNanager;
systemServiceManager 主要是对系统服进行创建、启动和生命局期管理,就会启动各种系统服。(android中最核心的服务 AMS 就是在 SystemServer 进程中启动的)
Launcher 进程: launcher 进程是android 系统启动的最后一步,主要负责将已经安装好的应用程序显示到界面上。
5.4 谷歌官方开机优化资料
https://source.android.com/devices/tech/perf/boot-times