Android系统启动系列

  1. Android系统启动系列----init进程
  2. Android系统启动系列----Zygote进程

引言

在开发app的过程中,是不是会有疑问:

  • java程序的运行不是从main方法开始么,怎么app入口是Application的onCreate方法?
  • 那java的运行环境虚拟机Dalvik VM和ART又是什么时候创建的?又是由谁创建的?
  • 安卓是Linux内核,那内核创建后系统又做了什么初始化了整个安卓环境?
  • 当我们的手机或者安卓系统设备按下电源按键的时候,系统都做什么?

当按下电源的那一刻都发生了啥:

今天的分析都是基于Android 6.0系统的分析。


第一步:启动电源

当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到RAM,然后执行。

第二步:执行引导程序(Boot Loader)

通常在运行Android系统之前会先执行Boot Loader引导程序,它不属于Android系统,常见的引导程序有:redboot、uboot、qi bootloader等等。或者自行开发引导程序,它是针对特定主板和芯片的,OEM制造厂商或者运营商在加锁的时候就对这个引导程序做修改,比如魅族就是修改了引导程序,所以刷不了机。

第三步:内核

Android内核与桌面linux内核启动的方式差不多。内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。

第四步:执行init进程

init进程是Android系统启动的第一个用户空间进程,init进程主要做两个事情。第一:挂载目录,如:挂载了/sys /dev /proc 等目录。第二:解析执行init.rc脚本文件。

系统编译,刷入手机后,init的进程保存在/system/core/bin目录中,对应程序的源代码入口是/system/core/init/init.cpp。

int main(int argc, char** argv) {if (!is_first_stage) {// Indicate that booting is in progress to background fw loaders, etc.close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));// 初始化属性服务property_init();// If arguments are passed both on the command line and in DT,// properties set in DT always have priority over the command-line ones.process_kernel_dt();process_kernel_cmdline();// Propogate the kernel variables to internal variables// used by init as well as the current required properties.export_kernel_boot_props();}........//  开始属性服务start_property_service();// 初始化“init.rc”配置文件解析器init_parse_config_file("/init.rc");action_for_each_trigger("early-init", action_add_queue_tail);........
}
复制代码

主要看init.rc脚本文件的解析,在说解析前,先来了解下配置脚本的内容,这是一个内建的脚本语言也叫Android初始化语言,有自己的语法结构,大概介绍下: Android初始化语言由四大类型的声明组成,即Actions(动作)、Commands(命令)、Services(服务)、以及Options(选项)。 Action(动作):动作是以命令流程命名的,有一个触发器决定动作是否发生。

on early-init# Set init and its forked children's oom_adj.write /proc/1/oom_score_adj -1000# Set the security context of /adb_keys if present.restorecon /adb_keysstart ueventdon initsysclktz 0# Backward compatibility.symlink /system/etc /etcsymlink /sys/kernel/debug /d# Link /vendor to /system/vendor for devices without a vendor partition.symlink /system/vendor /vendor# Create cgroup mount point for cpu accountingmkdir /acctmount cgroup none /acct cpuacctmkdir /acct/uid
复制代码

以上脚本中,on early-init、on init就是 Action类型的语句,语法格式为:

on <trigger> [&& <trigger>]*     //设置触发器  <command>  <command>      //动作触发之后要执行的命令
复制代码

Service(服务):服务是init进程启动的程序、当服务退出时init进程会视情况重启服务,语法格式为:

 service <name> <pathname> [ <argument> ]*   //<service的名字><执行程序路径><传递参数>  <option>       //option是service的修饰词,影响什么时候、如何启动services  <option>  ...
复制代码

下面是默认的init.rc文件,主要的事件及其服务。

Action/Service 描述
on early-init 设置init进程以及它创建的子进程的优先级,设置init进程的安全环境
on init 设置全局环境,为cpu accounting创建cgroup(资源控制)挂载点
on fs 挂载mtd分区
on post-fs 改变系统目录的访问权限
on post-fs-data 改变/data目录以及它的子目录的访问权限
on-boot 基本网络的初始化,内存管理等等
service servicemanager 启动系统管理器管理所有的本地服务,比如位置、音频、Shared preference等等…
service zygote 启动zygote进程

通常在这个阶段,我们可以在屏幕上看到“Android logo”字样或者图标。

我们重点来看看zygote进程相关的属性配置,它是独立的一个rc文件在/system/core/rootdir/init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-serverclass mainsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart mediaonrestart restart netdwritepid /dev/cpuset/foreground/tasks
复制代码

执行zygote程序,其实是通过执行app_process程序,然后传入xzygote等等参数实现的。先找到app_process程序的源码所在地:/frameworks/base/cmds/app_process/app_main.cpp 直接看程序的main函数:

int main(int argc, char* const argv[])
{if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {// Older kernels don't understand PR_SET_NO_NEW_PRIVS and return// EINVAL. Don't die on such kernels.if (errno != EINVAL) {LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));return 12;}}AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// Process command line arguments// ignore argv[0]argc--;argv++;int i;for (i = 0; i < argc; i++) {if (argv[i][0] != '-') {break;}if (argv[i][1] == '-' && argv[i][2] == 0) {++i; // Skip --.break;}runtime.addOption(strdup(argv[i]));}// Parse runtime arguments.  Stop at first unrecognized option.bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;++i;  // Skip unused "parent dir" argument.while (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;}}Vector<String8> args;if (!className.isEmpty()) {args.add(application ? String8("application") : String8("tool"));runtime.setClassNameAndArgs(className, argc - i, argv + i);} else {// We're in zygote mode.maybeCreateDalvikCache();if (startSystemServer) {args.add(String8("start-system-server"));}char prop[PROP_VALUE_MAX];if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",ABI_LIST_PROPERTY);return 11;}String8 abiFlag("--abi-list=");abiFlag.append(prop);args.add(abiFlag);// In zygote mode, pass all remaining arguments to the zygote// main() method.for (; i < argc; ++i) {args.add(String8(argv[i]));}}if (!niceName.isEmpty()) {runtime.setArgv0(niceName.string());set_process_name(niceName.string());}// 如果参数是--zygote,那么runtime.start执行zygote进程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.");return 10;}
}
复制代码

上面就是通过app_process进程,启动zygote进程的入口,执行启动zygote的程序的在java层的/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java,接下来我们看看:runtime.start("com.android.internal.os.ZygoteInit", args, zygote)都干了啥。 runtime是AppRuntime类的对象,start函数在其父类AndroidRuntime中声明和实现。AndroidRuntime是不是很熟悉了,Android的运行时,通常app异常的时候这玩意是不是总伴随你左右。原来这玩意这么早就启动在监控系统的一举一动了。

/** Start the Android runtime.  This involves starting the virtual machine* and calling the "static void main(String[] args)" method in the class* named by "className".** Passes the main function two arguments, the class name and the specified* options string.*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{....../* start the virtual machine */JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (startVm(&mJavaVM, &env, zygote) != 0) {  // 1return;}onVmCreated(env);   // 2/** Register android functions.*/if (startReg(env) < 0) {ALOGE("Unable to register all android natives\n");return;}/** We want to call main() with a String array with arguments in it.* At present we have two arguments, the class name and an option string.* Create an array to hold them.*/jclass stringClass;jobjectArray strArray;jstring classNameStr;stringClass = env->FindClass("java/lang/String");assert(stringClass != NULL);strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);assert(strArray != NULL);classNameStr = env->NewStringUTF(className);assert(classNameStr != NULL);env->SetObjectArrayElement(strArray, 0, classNameStr); for (size_t i = 0; i < options.size(); ++i) {jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());assert(optionsStr != NULL);env->SetObjectArrayElement(strArray, i + 1, optionsStr);}/** 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);  // 3if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");  // 4if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {env->CallStaticVoidMethod(startClass, startMeth, strArray);  // 5#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}......
}
复制代码
  1. 注释1:startVm顾名思义启动虚拟机,在此启动java虚拟机,当然这个是运行zygote进程的虚拟机,也就回答了文章最开始引言问题,虚拟机由app_process的AndroidRuntime创建。
  2. 注释2:虚拟机创建后的回调处理,主要是创建一些资源。
  3. 注释3:className就是app_process中传入的参数“com.android.internal.os.ZygoteInit”,因为ZygoteInit是java层的,所以需要使用jni来找到ZygoteInit.class,startClass就是ZygoteInit.class
  4. 注释4:通过jni的GetStaticMethodID函数获取到ZygoteInit.java的静态main方法的类似于反射的Mehod对象引用。
  5. 注释5:最后通过JNI的CallStaticVoidMethod函数类似于java反射的invoke方法,调用了4中获取的main方法的Method引用。 (如果对JNI不熟的可以看看JNI系列入门文章)

如上就是Zygote进程的启动方式。


总结

  1. 手机按下电源后,加载引导程序到内存中。
  2. 执行引导程序
  3. 启动内核,设置缓存、被保护存储器、计划列表,加载驱动,查找/system/core/bin中init程序文件。
  4. 启动init程序,挂载/sys /dev /proc等等目录,加载和解析init.rc脚本。
  5. 在加载init.rc脚本的时候,启动app_process进程。
  6. 在app_process进程中,根据init.zygote32.rc脚本配置的参数,启动zygote进程。
  7. 最终zygote进程的执行,即ZygoteInit.java文件main方法的执行,是由AndroidRuntime通过JNI的方式调用main方法执行的。在这之前启动了Dalvik VM或者ART虚拟机。

Android系统启动系列

  1. Android系统启动系列----init进程
  2. Android系统启动系列----Zygote进程

Android系统启动系列----init进程相关推荐

  1. Android系统启动流程--init进程的启动流程

    这可能是个系列文章,用来总结和梳理Android系统的启动过程,以加深对Android系统相对全面的感知和理解(基于Android11).  1.启动电源,设备上电 引导芯片代码从预定义的地方(固化在 ...

  2. Android系统启动流程—— init进程zygote进程SystemServer进程启动流程

    原文地址:https://blog.csdn.net/qq_30993595/article/details/82714409 Android系统启动流程 Android系统启动过程往细了说可以分为5 ...

  3. Android 10.0系统启动之init进程-[Android取经之路]

    摘要:init进程是linux系统中用户空间的第一个进程,进程号为1.当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取ini ...

  4. Linux-Android系统启动之INIT进程和system v init

    Linux系统启动之INIT进程和system v init 一. Linux系统启动之INIT进程和system v init 1. 首先介绍一下INIT进程  init进程在Start_kerne ...

  5. Android系统启动系列1 进程基础

    一 进程是什么? 1.1 什么是进程? 可以认为进程是一个程序的一次执行过程,或者说一个正在执行的程序.在这个过程中,伴随着资源的分配和释放.进程是资源管理的最小单元. "进程四要素"--<Li ...

  6. Android启动的init进程

    /system/core/init/- init.cpp- init_parser.cpp- signal_handler.cpp 一.概述 init是Linux系统中用户空间的第一个进程,进程号为1 ...

  7. 鸟人的Android揭秘(9)——Init进程运行过程

    众所周知,Linux中所有进程都是由init进程创建并运行起来的.首先Linux加载内核启动,然后在用户空间中启动init进程,之后init进程再依次启动系统运行的其它进程.在系统启动完成后,init ...

  8. Android程序暂停sh,init进程 解析Android启动脚本init.rc 修改它使不启动android init.rc中启动一个sh文件...

    Android启动后,系统执行的第一个进程是一个名称为init 的可执行程序.提供了以下的功能:设备管理.解析启动脚本.执行基本的功能.启动各种服务.代码的路径: system/core/init,编 ...

  9. Android系统启动流程4---init进程的工作流程

    概况: 启动init进程 运行init.rc 启动zygote服务 Zygote fork的第一个进程--SystemServer SystemServer启动系统服务 1. linux启动第一个应用 ...

最新文章

  1. SQL 数据库的使用
  2. linux搜索文件为1kb,Linux常用命令
  3. 关于collect2: cannot find ld的解决办法
  4. 【python教程入门学习】零基础想转行学python,过来人提醒大家几点
  5. Document cookie属性
  6. jquery easyui datagrid 获取Checked选择行(勾选行)数据
  7. Struts到JSF/Tapestry
  8. 一段程序看懂比特币原理
  9. (2015省赛系列--团体热身赛第二场)
  10. IDirectXFileData::GetData 在dx9c中没有了
  11. 计算机网络的权威杂志,科学网—晒个自己整理的计算机网络和通信方向可能相关的期刊列表...
  12. ie不能加载flash html,ie浏览器flash无法加载怎么修复_win7系统ie浏览器flash加载不了如何解决-系统城...
  13. 无人机倾斜摄影—三维建模和DSM,DEM,DOM(正射影像)的生成「CC(Smart3D)),Pix4d,Photoscan,Inpho」
  14. ir2113错误电路
  15. 利用MATLAB将图片转换成coe文件、TXT文件、mif文件、bin文件
  16. 蓝奏云PHP解析接口,蓝奏云下载地址解析API[直链]
  17. 进入BeOS的花花世界 系列七
  18. 【C语言】数组(一维数组、二维数组)
  19. Google | Google Kubernetes Engine 集群实战
  20. 命令行方式运行PHP脚本

热门文章

  1. int max+1小于0_INT_MAX常数,C ++中的示例
  2. java canvas画圆圈_java – 在视图上绘制一个圆圈(android)
  3. 计算机里的东西不小心删除如何恢复,原先在电脑界面上的文件不小心删除了怎么恢复,谢谢了...
  4. 【计算机网络】数据链路相关技术
  5. 面试题57 - II. 和为s的连续正数序列 golang
  6. 软件工程---4.需求工程
  7. c++中的deque容器
  8. NAU8810相关问题
  9. C++派生类含有成员对象构造函数析构函数顺序
  10. 【Java学习笔记四】Java中的包