Platform Information : System: Android5.1 Platform: Qualcomm msm8916 Author: Andy Lee Email: huaqianlee@gmail.com
如有错误欢迎指出,共同学习,共同进步 在我第一次接触Android得时候,我就很想知道Android设备在按下电源键后是怎么启动到主界面的,但是到现在为止也没有完全理清这个过程,所以就决定从按下power键开始来分析一下这个流程。虽然Android基于Linux内核开发的一个操作系统,但是在init进程后Android附加了很多其他操作,所以其启动流程还是有比较大的差别的,关于Linux系统的启动流程可以参考我的另一篇博文:深入理解linux启动过程 。
因为我现在工作中用到的是高通的源码,并且高通也是目前Android手机的主流芯片,所以我就按照高通的msm8916来分析了,不过其他的也应该大同小异。 首先来看一下官方给出的Android系统架构:
当按下电源开关后,主要执行了如下步骤: 另,在内核启动了第一个进程后,init->home lanucher的详细流程如下:
注:此图取自网络,觉得描述得很详细,故附上
接下来就按照引导程序、内核启动、init进程、系统服务、Home Lanucher这样的顺序来分析Android启动的code。
引导程序 引导程序在Android操作系统开始运行前的一个小程序,其主要为内核启动服务。引导程序执行的第一段代码,因此它是针对特定的主板与芯片的。设备制造商要么使用很受欢迎的引导程序比如redboot、uboot或者开发自己的引导程序,它不是Android操作系统的一部分。引导程序是OEM厂商或者运营商加锁和限制的地方。
引导程序分两个阶段执行。第一个阶段,检测外部的RAM以及加载对第二阶段有用的程序;第二阶段,引导程序设置网络、内存等等。这些对于运行内核是必要的,为了达到特殊的目标,引导程序可以根据配置参数或者输入数据设置内核。
power-on及系统启动 当按下电源键或者系统重启之后,引导芯片代码PBL(Primary Boot Loader,类似于x86的BIOS)从预定义的地方(固化在ROM)开始执行,PBL由高通做好了的烧写在芯片中,PBL将启动设备、支持紧急下载等,然后加载引导程序sbl1,然后跳转到sbl1执行。
处理器启动地址 MSM8916芯片内部有很多不同的处理器,如下:
子系统
处理器
启动地址
APPS
Cortex-a53
0xfc010000
RPM
Cortex-m3
0x00200000/0x0
Modem
MSS_QDSP6
可配置的
Pronto
ARM9TM
0x0/0xffff0000/硬件重映射
启动栈
组件
处理器
加载源地址
执行地址
功能
APPS PBL
Cortex-A53
NA
APPS ROM
启动设备,检测接口,支持紧急下载,通过L2TCM加载和校验SBL1 ELF段,加载校验RPM code RAM
SBL1
Cortex-A53
eMMC
L2 TCM(segment1)/OCIMEM/RPM code RAM(segment2)
初始化内存子系统(总线,DDR,时钟,CDT),加载校验TZ、Hyperviser、RPM_FW、APPSBL镜像,通过USB2.0和Sahara协议memory dump,看门狗调试retention(如:L2 flush),RAM dump到eMMC/SD卡等的支持,大容量存储支持,USB驱动支持,USB充电,温度检测,PMIC驱动的支持,配置DDR以及crash调试的flush L1/L2/ETB支持等相关配置
QSEE/TZ
Cortex-A53
eMMC
LPDDR2/3
等同于TZBSP,设置运行时安全环境,配置xPU,支持fuse驱动,校验子系统镜像,丢弃RESET调试功能
QHEE(Hypervisior)
Cortex-A53
eMMC
LPDDR2/3
Hypervisor镜像负责设置VMM,配置SMMU以及控制xPU存取
RPM_FW
Cortex-M3
eMMC
RPM code RAM
电源资源管理
APPSBL/启动管理器和系统加载器
Cortex-A53
eMMC
LPDDR2/3
启动画面,加载校验内核
HLOS
Cortex-A53
eMMC
LPDDR2/3
引导HLOS镜像,例如a53 HLOS内核镜像,Pronto镜像等
Modem PBL
MSS_QDSP6
NA
Modem ROM HexagonTM TCM
设置Hexagon TCM,从LPDDR2/3拷贝MBA到Hexagon TCM并校验
MBA
MSS_QDSP6
eMMC
Hexagon TCM
校验modem镜像,xPU为modem和memory dump保护DDR
eMMC :Embeded Multi Media Card,内嵌式记忆体,内部存储APPS PBL :Application Processor Primary Boot Loader,应用处理器初级引导程序SBL1 :Secondary Boot Loader Stage1,第二引导程序阶段一(此处写阶段一是因为早期高通芯片分为几个阶段,但现在都由sbl1实现)TZ :TrustZonePRM_FW :Resource Power Manager Firmware,电源资源管理固件HLOS :High-Level Operating System,高级操作系统Modem PBL :Modem Primary Boot Loader,调制解调器侧初级引导程序MBA :Modem Boot Authenticator,调制解调器侧引导校验程序
引导代码流程
系统上电或者MSM8916 AP侧CPU重启。
Cortex-A53中APPS PBL执行,从启动设备中加载校验是sbl1镜像,然后跳转到sbl1中执行。
sbl1初始化ddr,从启动设备中加载校验QSEE/TZ、QHEE、RPM_FW、APPSBL镜像到DDR。
sbl1将控制权给QSEE/TZ,QSEE/TZ将设置一个安全环境,配置xPU,并支持fuse驱动。
QSEE传递控制权给QHEE,QHEE负责设置VMM,配置SMMU和xPU存取控制。
QHEE通知RPM开始执行RPM固件。
QHEE将控制器传递给HLOS APPSBL,APPSBL将初始化系统。
HLOS APPSBL加载和校验HLOS内核。
HLOS内核通过PIL.Modem加载MBA和modem镜像到DDR,然后继续启动进程。
HLOS通过PIL加载外围设备镜像Pronto到DDR,在通过TZ校验。
第一阶段引导程序和第二阶段引导程序 由PBL加载的sbl1是第一阶段引导程序,APP SBL为第二阶段引导程序。这两部分代码的作用在上面启动栈 和引导代码流程 中已有一个简单的描述,如果想了解更多请参考我另外两篇博文:
Android源码bootable解析之bootloader LK(little kernel) 高通平台Android源码bootloader分析之sbl1(一)
内核 Android的内核就是用的Linux的内核,只是针对移动设备做了一些优化,所有Android内核与linux内核启动的方式差不多。内核主要设置缓存、被保护存储器、计划列表,加载驱动等。当内核完成这些系统设置后,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。这部分可以参考我的另一篇博文:
深入理解linux启动过程
init进程 init进程时Android的第一个用户空间进程,是所有进程的父进程。init进程主要有两个任务,一是挂载目录,比如/sys、/dev、/proc,二是读取解析init.rc脚本,将其中的元素整理成自己的数据结构(链表)。
init进程实现路径: system\core\init
init.c 首先来看一下init进程的实现代码init.c, 其关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 # system\core\init\init.c int main (int argc, char **argv) { ... mkdir("/dev" , 0755 ); mkdir("/proc" , 0755 ); mkdir("/sys" , 0755 ); mount("tmpfs" , "/dev" , "tmpfs" , MS_NOSUID, "mode=0755" ); mkdir("/dev/pts" , 0755 ); mkdir("/dev/socket" , 0755 ); mount("devpts" , "/dev/pts" , "devpts" , 0 , NULL ); mount("proc" , "/proc" , "proc" , 0 , NULL ); mount("sysfs" , "/sys" , "sysfs" , 0 , NULL ); ... property_init(); get_hardware_name(hardware, &revision); process_kernel_cmdline(); ... selinux_initialize(); init_parse_config_file("/init.rc" ); action_for_each_trigger("early-init" , action_add_queue_tail); queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done" ); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng" ); queue_builtin_action(keychord_init_action, "keychord_init" ); queue_builtin_action(console_init_action, "console_init" ); action_for_each_trigger("init" , action_add_queue_tail); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng" ); queue_builtin_action(property_service_init_action, "property_service_init" ); queue_builtin_action(signal_init_action, "signal_init" ); if (is_charger) { action_for_each_trigger("charger" , action_add_queue_tail); } else { if (is_ffbm) { action_for_each_trigger("ffbm" , action_add_queue_tail); } else { action_for_each_trigger("late-init" , action_add_queue_tail); } } queue_builtin_action(queue_property_triggers_action, "queue_property_triggers" ); for (;;) { ... execute_one_command(); restart_processes(); # 监听属性服务事件 ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ... #if BOOTCHART #endif nr = poll(ufds, fd_count, timeout); # 处理具体消息 handle_property_set_fd(); handle_keychord(); handle_signal(); ... }
init.rc .rc文件的语法 init.rc文件是Android的有特定格式和规则的脚本文件,位于:system\core\rootdir\init.rc,称为Android的初始化语言。当进入adb shell后,我们能在根目录看到一个只读的虚拟内存文件init.rc,源文件init.rc被打包在boot.img中ramdisk.img中。其有四类声明:
Action - 动作
Command - 命令
Service - 服务
Option - 选项
该语言规定,Action和Service是以一种“小节”(Section)的形式出现的,其中每个Action小节可以含有若干Command,而每个Service小节可以含有若干Option。小节只有起始标记,却没有明确的结束标记,也就是说,是用“后一个小节”的起始来结束“前一个小节”的。
脚本中的Action大体上表示一个“动作”,它用一系列Command共同完成该“动作”。Action需要有一个触发器(trigger)来触发它,一旦满足了触发条件,这个Action就会被加到执行队列的末尾。Action的形式如下:
1 2 3 4 on <trigger> <command1> <command2> ......
Service表示一个服务程序,会在初始化时启动,当服务退出时init进程会视情况重启服务。因为init.rc脚本中描述的服务往往都是核心服务,所以(基本上所有的)服务会在退出时自动重启。Service的形式如下:
1 2 3 4 service <name> <pathname> [<arguments>]* <option> <option> ......
其实,除了Action和Service,init.rc中还有一种小节:import小节。该小节类似java中的import或者c中的头文件,导入其他.rc脚本文件。如下:
1 2 3 4 import /init.environ.rc import /init.usb.rc import /init.${ro.hardware} .rc import /init.trace.rc
init.rc init.rc脚本的主要内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import /init.environ.rc import /init.usb.rc import /init.${ro.hardware} .rc import /init.${ro.zygote} .rc import /init.trace.rc import /init.ideanfc.preinstall.rc on early-init on init ... on load_all_props_action load_all_props on late-init trigger early-fs trigger fs trigger post-fs trigger post-fs-data trigger load_all_props_action ... trigger early-boot trigger boot on post-fs on post-fs-data on boot ... chown radio system /sys/power/wake_lock // 修改文件用户组 chown radio system /sys/power/wake_unlock chmod 0660 /sys/power/wake_lock // 修改文件操作权限 chmod 0660 /sys/power/wake_unlock ... class_start core service healthd /sbin/healthd service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart healthd onrestart restart zygote onrestart restart media ...
servicemanager主要注册获取服务,源码路径:frameworks\base\cmds\servicemanager\Service_manager.c。
回调函数 Action包含的不同command对应不同func回调函数,具体对应情况可查看Keywords.h,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # system\core\init\keywords.h int do_chroot (int nargs, char **args) ; int do_chdir (int nargs, char **args) ;... int do_write (int nargs, char **args) ;int do_copy (int nargs, char **args) ;int do_chown (int nargs, char **args) ;int do_chmod (int nargs, char **args) ;... KEYWORD(capability, OPTION, 0 , 0 ) KEYWORD(chdir, COMMAND, 1 , do_chdir) KEYWORD(chroot, COMMAND, 1 , do_chroot) KEYWORD(class, OPTION, 0 , 0 ) KEYWORD(class_start, COMMAND, 1 , do_class_start) KEYWORD(class_stop, COMMAND, 1 , do_class_stop) KEYWORD(class_reset, COMMAND, 1 , do_class_reset) KEYWORD(console, OPTION, 0 , 0 ) ... KEYWORD(user, OPTION, 0 , 0 ) KEYWORD(wait, COMMAND, 1 , do_wait) KEYWORD(write, COMMAND, 2 , do_write) KEYWORD(copy, COMMAND, 2 , do_copy) KEYWORD(chown, COMMAND, 2 , do_chown) KEYWORD(chmod, COMMAND, 2 , do_chmod) ...
init.rc脚本文件的解析 关于init.rc脚本文件的解析,就不详细描述了,只列出关键文件和关键函数,如下:
1 2 3 4 5 6 7 8 9 10 11 # system\core\init\init.c init_parse_config_file("/init.rc" ); data = read_file(fn, 0 ); parse_config(fn, data); # system\core\init\init_parser.c ,被parse_config调用 lookup_keyword() kw_is() parse_new_section() state.parse_line() init_parse_config_file(import->filename)
core服务和main服务 boot子阶段会通过class_start对应的回调函数do_class_start开启core服务和main服务,这两类服务通过如下两句表明身份:
1 2 class core # 声明section为core服务 class main # 声明section为main服务
core服务
core类型的服务
对应的可执行文件
说明
ueventd
/sbin/ueventd
logd
/system/bin/logd
healthd
/sbin/healthd
电源管理服务
console
/system/bin/sh
adbd
/sbin/adbd
servicemanager
/system/bin/servicemanager
service manager service服务,Android的核心之一,zygote在此服务中加载
vold
/system/bin/vold
main服务
main类型的服务
对应的可执行文件
说明
netd
/system/bin/netd
debuggerd
/system/bin/debuggerd
ril-daemon
/system/bin/rild
surfaceflinger
/system/bin/surfaceflinger
zygote
/system/bin/app_process
Android创建内部创建新进程的核心服务
drm
/system/bin/drmserver
media
/system/bin/mediaserver
bootanim
/system/bin/bootanimation
installd
/system/bin/installd
flash_recovery
/system/etc/install-recovery.sh
racoon
/system/bin/racoon
mtpd
/system/bin/mtpd
keystore
/system/bin/keystore
dumpstate
/system/bin/dumpstate
sshd
/system/bin/start-ssh
mdnsd
/system/bin/mdnsd
属性服务 众所周知在windows中有一个注册表机制,在注册表中提供了大量的key-value属性。在Android(或Linux)中也有类似的机制:属性服务(property service)。init在启动的过程中会启动属性服务(Socket服务),并且在内存中建立一块存储区域,用来存储这些属性。当读取这些属性时,直接从这一内存区域读取,如果修改属性值,需要通过Socket连接属性服务完成。在init.c文件中main函数通过property_service_init_action调用了start_property_service函数来启动属性服务。
属性文件是由系统依次读取位于不同目录的配置文件,关于属性文件的解析也涉及到很多内容,这里就不去详细分析了,关键函数和路径如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void start_property_service(void) const char* property_get(const char *name) const prop_info *__system_property_find(const char *name) static int init_property_area(void) static int send_prop_msg(prop_msg *msg) int __system_property_set(const char *key, const char *value)
另,我们可以在adb shell中通过getprop获取所有属性名,或者通过getprop < 根属性名>获取具体属性值,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 C:\Users\Administrator>adb shell shell@msm8916_32:/ $ getprop getprop [DEVICE_PROVISIONED]: [1] [audio.dolby.ds2.enabled]: [true ] [audio.offload.buffer.size.kb]: [64] [audio.offload.gapless.enabled]: [true ] [audio.offload.min.duration.secs]: [30] [av.offload.enable]: [true ] [bluetooth.hfp.client]: [1] ... shell@msm8916_32:/ $ getprop ro.build.product getprop ro.build.product msm8916_32
Zygote Zygote是Android中非常重要十分核心的一个服务,将由其去运行系统服务及孵化Activity进程等,接下来就好好分析一下Zygote。
在Java中,不同的虚拟机实例会为不同的应用分配不同的内存,每一个实例都有它自己的核心库类文件和堆对象的拷贝。但Android系统如果为每一个应用启动不同的Dalvik虚拟机实例,就会消耗大量的内存以及时间。因此,为了克服这个问题,Android系统创造了”Zygote”。Zygote让Dalvik虚拟机共享代码、低内存占用以及最小的启动时间成为可能。Zygote是一个虚拟器进程,在系统引导的时候启动。Zygote预加载以及初始化核心库类。通常,这些核心类是只读的,也是Android SDK或者核心框架的一部分。
Zygote的启动 首先,先看一下Zygote在相关zygote.rc文件中的定义:
1 2 3 4 5 6 7 service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd
当init.c中解析了rc文件后,rc文件中定义class_start命令对应do_class_start函数将启动服务(包括Zygote)进程,关键源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 int do_class_start(int nargs, char **args) { /* Starting a class does not start services * which are explicitly disabled. They must * be started individually. */ service_for_each_class(args[1], service_start_if_not_disabled); // 从service_list链表找到class_name和参数一致的,然后调用service_start_if_not_disabled启动服务 return 0; } static void service_start_if_not_disabled(struct service *svc) { if (!(svc->flags & SVC_DISABLED)) { service_start(svc, NULL); //启动服务 } else { svc->flags |= SVC_DISABLED_START; } } void service_start(struct service *svc, const char *dynamic_args) { ... /* 检查需要开启进程的可执行文件是否存在,如:Zygote路径/system/bin/app_process */ if (stat (svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n" , svc->args[0], svc->name); svc->flags |= SVC_DISABLED; return ; } ... pid = fork(); // 创建子进程,父进程为init创建的service进程 ... execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); // 执行进程,如/system/bin/app_process ... if (properties_inited()) notify_service_state(svc->name, "running" ); // 设置服务为running状态 }
经过上述流程,app_proces等进程就被启动起来了就进入到app_process相关code了,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 # frameworks\base\cmds\app_process\app_main.cpp int main (int argc, char * const argv[]) { ... AppRuntime runtime (argv[0 ], computeArgBlockSize(argc, argv)) ; ... if (zygote) { runtime.start ("com.android.internal.os.ZygoteInit" , args); } ... } # frameworks\base\core\jni\AndroidRuntime.cpp void AndroidRuntime::start (const char * className, const Vector<String8>& options) { ... JniInvocation jni_invocation; jni_invocation.Init (NULL ); JNIEnv* env; if (startVm (&mJavaVM, &env) != 0 ) { return ; } onVmCreated (env); ... if (startReg (env) < 0 ) { ALOGE ("Unable to register all android natives\n" ); return ; } ... char * slashClassName = toSlashClassName (className); jclass startClass = env->FindClass (slashClassName); jmethodID startMeth = env->GetStaticMethodID (startClass, "main" ,"([Ljava/lang/String;)V" ); env->CallStaticVoidMethod (startClass, startMeth, strArray);
这样就真正进入了Zygote进程了,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main(String argv[]) { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); ... registerZygoteSocket(socketName); // 为Zygote注册服务器套接字(server socket) ... preload(); // 调用preloadClassed():加载一系列类的文本文件(“preloaded-classes”),位于/frameworks/base 调用preloadResources(): 加载本地主题、布局以及android.R文件中包含的所有东西 ... startSystemServer(abiList, socketName); // 准备参数,通过Zygote孵化新 system server 进程 runSelectLoop(abiList); // 运行Zygote进程选中的loop,此函数中不断接受新的connections,并读取command 执行 ...
到了这个阶段,就可以看到启动动画了。前面分析了Zygote的流程,可以总结为如下一张图:
在rc文件中有通过onrestart定义需要重启的动作或服务,这块就不去详细分析了,只将重启流程中的关键函数和路径列出:
1 2 3 4 5 6 7 8 9 10 queue_builtin_action(signal_init_action, "signal_init" ); //main函数中 static int signal_init_action(int nargs, char **args) //此函数调用singal_init void signal_init(void) static void sigchld_handler(int s) int get_signal_fd() void handle_signal(void) static int wait_for_one_process(int block) // 此函数中将发出restarting信号,然后init.c中的main函数收到此信号后将重启相应进程
Home Lanucher启动 上ZygoteInit.java中mian函数在loop之前会调用一个关键函数startSystemServer,其除了准备一些参数外还将fork进程。其中就包括SystemServer,在SystemServer中最终会调用到ActivityManagerService,然后Home Lanucher就由ActivityManagerService中的方法来启动。关键源码和路径如下:
1 2 3 4 5 6 7 8 9 10 11 public void systemReady(final Runnable goingCallback) { // Start up initial activity. mBooting = true ; startHomeActivityLocked(mCurrentUserId, "systemReady" ); } boolean startHomeActivityLocked(int userId, String reason) { setDefaultLauncher(); // 第一次开机时设置 mStackSupervisor.startHomeActivity(intent, aInfo, reason); // 开启homeActivity }
花了这么长的时间,终于把这个流程走完了。 不过还是有很多地方偷懒了,没有详细研究,只了解了一个大概,然后做了记录。如有错误请谅解!
Reference 在我分析启动流程时,主要参考引用了如下地址,后3篇博文对init.c和init.rc分析得十分详细,感兴趣可以参考一下这几篇博文。Android官网 Android设备启动流程 Android 4.4的init进程 Android情景分析之详解init进程 Androidinit过程详解
本文边分析边记录而成,由于时间原因,很多地方没有详细分析,简单看了一下就跳过。可能会有很多描述不清楚甚至错误的地方,欢迎指出,共同学习,共同进步。