Android完整的启动流程,可以理解为从按下开机键到用户最终看到Launcher的过程,这部分细节很多,力求了解大概流程,对关键细节掌握即可。本篇重点讲解从开机到创建Dalvik VM的过程,下篇分析从Zygote到最终Launcher的显示过程。

介绍Android系统启动流程的文章很多,在正式介绍之前,我们可以思考下,类比windows等PC系统的系统启动流程,Android系统的启动流程有何特别之处。

Android可以理解为构建于在Linux上的一个特殊“应用”,因此Android启动的流程除了kernel的启动部分之外,还有构建在它之上的Android运行环境的启动部分。本篇主要介绍Kernel启动到构建出Dalvik VM的过程。

借用一张图:

加电 & 系统自检

当系统加电后,CPU复位会首先运行在ROM芯片内固化的一段指令(Boot ROM),这段指令会将BootLoader程序加载到内存中并且开始执行。

BootLoader

BootLoader也叫“引导加载程序”,是个底层代码,包含一堆指令,主要分为两部分,

  • 第一部分,检测外部RAM,找到并加载另一段bootloader程序,之后跳到其中执行。
  • 第二部分bootloader,设置运行内核所需的网络、内存等基本条件,之后找到对应的Kernel镜像文件,并将其加载到物理内存中。

关于镜像文件,可以参考:https://source.android.com/devices/bootloader/partitions-images

bootloader相关的源码见:android/platform/bootable/bootloader/legacy/usbloader

经过这一步,Kernel的相关镜像已经加载到了物理内存的指定地址处,并建立了内核运行所需的基本环境。接下来BootLoade就将控制权交给了Kernel,内核开始执行

Kernel start阶段

android kernel的加载过程与Linux Kernel加载过程类似,随着内核启动,开始设置缓存、受保护内存、调度和加载驱动程序。当完成这些设置后,便会启动指定 /system/core/init 第一个用户进程init。

相比Linux Kernel,Android Kernel新增了一些特性

  • Binder:android新增的一种进程间通信机制;
  • Ashmem:android新增的共享内存方式;
  • Logger:对LogCat的内核支持;
  • WakeLocks:电源管理;
  • OOM处理:可用内存过低,会杀死进程;
  • Alarm Manager: 通过用户控件通知内核何时唤醒;
  • YAFFS2:针对闪存设备的文件系统;关于Android的文件系统,我之前也有一篇专门介绍

init进程

init进程是Android系统的第一个用户进程,可以说是root进程,它主要有两个职责:

  1. 挂载/sys,/dev,/proc等文件目录;
  2. 解析运行init.rc中的相关配置;init.rc脚本相关的语法可参考:https://android.googlesource.com/platform/system/core/+/refs/heads/android10-release/init

相关源码

以android 10源码为例,init进程的启动主要分为几个阶段:

//init进程入口/init/main.cppint main(int argc, char** argv) {f (argc > 1) {if (!strcmp(argv[1], "subcontext")) {android::base::InitLogging(argv, &android::base::KernelLogger);const BuiltinFunctionMap function_map;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);}}//第一阶段return FirstStageMain(argc, argv);}//第一阶段/init/first_stage_init.cppint FirstStagetMain(int argc, char** argv) {//主要是创建挂载相关文件目录CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));CHECKCALL(mkdir("/dev/pts", 0755));CHECKCALL(mkdir("/dev/socket", 0755));CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));#define MAKE_STR(x) __STRING(x)CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));...//------------执行selinux_setup-->main.cppconst char* path = "/system/bin/init";const char* args[] = {path, "selinux_setup", nullptr};execv(path, const_cast<char**>(args));...}//建立安全机制/init/selinux.cpp: 执行一些安全策略/ This function initializes SELinux then execs init to run in the init SELinux context.int SetupSelinux(char** argv) {// Set up SELinux, loading the SELinux policy.SelinuxSetupKernelLogging();SelinuxInitialize();//--------------进入second_stage main.cppconst char* path = "/system/bin/init";const char* args[] = {path, "second_stage", nullptr};execv(path, const_cast<char**>(args));}//第二阶段/init/init.cppint SecondStageMain(int argc, char** argv) {//初始化日志系统InitKernelLogging(argv);//初始化属性域property_init();//装载子进程信号处理:为了防止僵尸子进程无法回收InstallSignalFdHandler(&epoll);//开启属性服务StartPropertyService(&epoll);//加载脚本ActionManager& am = ActionManager::GetInstance();ServiceList& sm = ServiceList::GetInstance();//加载解析init.rc脚本LoadBootScripts(am, sm);...}//解析init.rc文件static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {//构造一个解析器Parser parser = CreateParser(action_manager, service_list);std::string bootscript = GetProperty("ro.boot.init_rc", "");if (bootscript.empty()) {parser.ParseConfig("/init.rc");}...}

解析init.rc

init.rc是个配置文件,具体语法可以参考:
https://android.googlesource.com/platform/system/core/+/refs/heads/android10-release/rootdir/init.rc

在手机目录下也可以找到针对32位和64位的rc文件:

这里不分析init.rc文件的细节,解析init.rc后主要完成了以下几件事情:

  • 创建一些关键文件目录、设置权限策略;
  • 开启ServiceManager、VndServiceManager等本地守护服务;
  • fork出Zygote进程;

Zygote进程

通过解析init.rc,从init进程fork出Zygote, 并且指定启动入口在app_main.cpp

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
//----Zygote进程入口-----/frameworks/base/cmds/app_process/app_main.cppint main(int argc, char* const argv[]){//构造AppRuntimeAppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));argc--;argv++;//while循环拼接参数:根据init.rc中的配置,这里zygote=truewhile (i < argc) {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;} else if (strcmp(arg, "--application") == 0) {application = true;} else if (strncmp(arg, "--nice-name=", 12) == 0) {niceName.setTo(arg + 12);} else if (strncmp(arg, "--", 2) != 0) {className.setTo(arg);break;} else {--i;break;}}//根据前面init.rc指定传入参数,if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) {runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");}}//AndroidRuntime 继承自 AppRuntime/frameworks/base/core/jni/AndroidRuntime.cppAndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :mExitWithoutCleanup(false),mArgBlockStart(argBlockStart),mArgBlockLength(argBlockLength){//初始化绘制引擎skiaSkGraphics::Init();// 虚拟机参数mOptions.setCapacity(20);//一个进程gCurRuntime只能初始化一次assert(gCurRuntime == NULL);        // one per processgCurRuntime = this;}void AndroidRuntime::start(const char className, const Vector <String8> & options, bool zygote){.../* start the virtual machine *///初始化JNIJniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv env;//启动VMif (startVm(&mJavaVM, &env, zygote) != 0) {return;}//创建成功调用onVmCreated(env);/** 注册预定义的JNI*/if (startReg(env) < 0) {return;}/** Start VM.  This thread becomes the main thread of the VM, and will* not return until the VM exits.*///入口类char slashClassName = toSlashClassName(className);jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {} else {//找到入口方法, ZygoteInit.main()jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");    if (startMeth == NULL) {    } else {    //Native调用Java,至此进入Java    env->CallStaticVoidMethod(startClass, startMeth, strArray);    #if 0    if (env->ExceptionCheck())    threadExitUncaughtException(env);    #endif    }    }free(slashClassName);ALOGD("Shutting down VM\n");if (mJavaVM->DetachCurrentThread() != JNI_OK)    ALOGW("Warning: unable to detach main thread\n");if (mJavaVM->DestroyJavaVM() != 0)    ALOGW("Warning: VM did not shut down cleanly\n");}

小结:init通过解析init.rc后fork出Zygote进程,之后Zygote在其main中处理完一堆参数后,初始化出一个AndroidRuntime对象,在构造AndroidRuntime对象时就会初始化Skia绘制引擎,然后调用start创建Dalvik(ART)虚拟机,注册上层需要的JNI函数,找到并调用Java层入口类ZygoteInit.main()。至此,进入了Java世界。

总结

Android Kernel启动过程与大多数系统启动类似,从Boot ROM(PC BIOS)–> BootLoader–>Kernel 自启–>第一个进程(init);只不过android Kernel相比Linux,多了一些Ashmem、Binder驱动之类的,同时需要为构建Android上层环境做准备,因此init在解析init.rc文件的配置过程中,会启动一些守护服务,同时fork出Zygote进程,Zygote作为连接Kernel与上层世界的桥梁,这里创建处了Dalvik(ART) VM,注册好JNI方法,通过Native调用并加载ZygoteInit.main(),进入了真正的Java世界。

下篇继续分析从ZygoteInit.mai()到Launcher界面的显示

更多相关文章

  1. 一款霸榜 GitHub 的开源 Linux 资源监视器!
  2. 移动端启动速度
  3. android使用logwrapper进行log重定向
  4. 抖音BoostMultiDex优化实践:Android低版本上APP首次启动时间减少8
  5. 在Xamarin.Forms中使用SkiaSharp绘图时从原生工程中加载图片
  6. android开发实例02:列表字母索引与过滤检索
  7. Android百度语音集成——文字转语音
  8. Android(安卓)IPC 进程间通信实现理解
  9. android 对于asset和raw下文件的操作

随机推荐

  1. 用 Hypothesis 来自动化单元测试
  2. Python_学习之流程控制
  3. Python_学习之运算符
  4. 课程学习记录之python迭代器和生成器
  5. 一个非常简单好用的 Python 图形界面库
  6. 我就是这样学 Python 的
  7. Docker | Docker技术基础梳理(八) - Dock
  8. Docker | Docker技术基础梳理(总结篇)附
  9. 零基础,从一个抢票程序,提升自己的Python技
  10. Flask | Web开发基础提要