引言
在开发app的过程中,是不是会有疑问:
- java程序的运行不是从main方法开始么,怎么app入口是Application的onCreate方法?
- 那java的运行环境虚拟机Dalvik VM和ART又是什么时候创建的?又是由谁创建的?
- 安卓是Linux内核,那内核创建后系统又做了什么初始化了整个安卓环境?
- 当我们的手机或者安卓系统设备按下电源按键的时候,系统都做什么?
当按下电源的那一刻都发生了啥:
Android系统启动流程
今天的分析都是基于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_keys
start ueventd
on init
sysclktz 0
# Backward compatibility.
symlink /system/etc /etc
symlink /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 accounting
mkdir /acct
mount cgroup none /acct cpuacct
mkdir /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-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
writepid /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) { // 1
return;
}
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); // 3
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"); // 4
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray); // 5
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
......
}
- 注释1:startVm顾名思义启动虚拟机,在此启动java虚拟机,当然这个是运行zygote进程的虚拟机,也就回答了文章最开始引言问题,虚拟机由app_process的AndroidRuntime创建。
- 注释2:虚拟机创建后的回调处理,主要是创建一些资源。
- 注释3:className就是app_process中传入的参数“com.android.internal.os.ZygoteInit”,因为ZygoteInit是java层的,所以需要使用jni来找到ZygoteInit.class,startClass就是ZygoteInit.class
- 注释4:通过jni的GetStaticMethodID函数获取到ZygoteInit.java的静态main方法的类似于反射的Mehod对象引用。
- 注释5:最后通过JNI的CallStaticVoidMethod函数类似于java反射的invoke方法,调用了4中获取的main方法的Method引用。
如上就是Zygote进程的启动方式。
总结
- 手机按下电源后,加载引导程序到内存中。
- 执行引导程序
- 启动内核,设置缓存、被保护存储器、计划列表,加载驱动,查找/system/core/bin中init程序文件。
- 启动init程序,挂载/sys /dev /proc等等目录,加载和解析init.rc脚本。
- 在加载init.rc脚本的时候,启动app_process进程。
- 在app_process进程中,根据init.zygote32.rc脚本配置的参数,启动zygote进程。
- 最终zygote进程的执行,即ZygoteInit.java文件main方法的执行,是由AndroidRuntime通过JNI的方式调用main方法执行的。在这之前启动了Dalvik VM或者ART虚拟机。
网友评论