Android 分区
- /boot:存放引导程序,包括内核和内存操作程序。
- /system:相当于电脑 C 盘,存放 Android 系统及系统应用。
- /recovery:恢复分区,可以进入该分区进行系统恢复。
- /data:用户数据区,包含了用户的数据:联系人、短信、设置、用户安装的程序。
- /cache:安卓系统缓存区,保存系统最常访问的数据和应用程序。
- /misc:包含一些杂项内容,如系统设置和系统功能启用禁用设置。
- /sdcard:用户自己的存储区,可以存放照片,音乐,视频等文件。
开机加载过程
与计算机启动过程类似,当按下电源按键后,引导芯片代码开始从预定义的地方(固化在 ROM 中的预设代码)开始执行,芯片上的 ROM 会寻找 Bootloader 代码,并加载到内存(RAM)中。
接着 Bootloader 开始执行,Bootloader 会读取 ROM 找到操作系统并将 Linux 内核加载到 RAM 中。
当 Linux 内核启动后会初始化各种软硬件环境,加载驱动程序,挂载根文件系统,Linux 内核加载的最后阶段会启动并执行第一个用户空间进程 init 进程。
Linux 内核启动过程主要涉及 3 个特殊的进程,swapper 进程(又称为 idle 进程,PID = 0), init 进程(PID = 1)和 kthreadd 进程(PID = 2),这三个进程是内核的基础。
-
idle 进程是 Linux 系统第一个进程,是 init 进程和 kthreadd 进程的父进程,这个进程在完成初始化工作后,就开启了init和kthreadd进程,然后自己进入无限循环,负责进程的调度。
-
init 进程是 Linux 系统第一个用户进程,是 Android 系统应用程序的始祖。init进程分为前后两部分,前一部分是在内核启动的,主要是完成创建和内核初始化工作,内容都是跟Linux内核相关的;后一部分是在用户空间启动的,主要完成Android系统的初始化工作。
-
kthreadd 这个进程由idle通过kernel_thread 创建,负责所有内核线程的调度和管理。
idle
idle负责完成底层硬件和系统资源的初始化,他其实就是内核初始化用的start_kernel()这个函数所在进程,在start_kernel()执行的末尾,他会把本进程进入循环,处理进程调度,这个时候idle就显示出现了。
在start_kernel()所在进程执行完成之前,也就是idle显示出现之前,会调用函数rest_init(),来启动init进程和kthreadd进程
rest_init
rest_init最重要的就是通过kernel_thread创建了init和kthreadd进程
static noinline void __init_refok rest_init(void)
{
int pid;
/*
* 1.C语言中const相当于Java中的final static, 表示常量
* 2.struct是结构体,相当于Java中定义了一个实体类,里面只有一些成员变量,{.sched_priority =1 }相当于new,
* 然后将成员变量sched_priority的值赋为1
*/
const struct sched_param param = { .sched_priority = 1 }; //初始化优先级为1的进程调度策略,
//取值1~99,1为最小
rcu_scheduler_starting(); //启动RCU机制,这个与后面的rcu_read_lock和rcu_read_unlock是配套的,用于多核同步
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
/*
* 1.C语言中支持方法传参,kernel_thread是函数,kernel_init也是函数,但是kernel_init却作为参数传递了过去,
* 其实传递过去的是一个函数指针,参考[函数指针](http://www.cnblogs.com/haore147/p/3647262.html)
* 2.CLONE_FS这种大写的一般就是常量了,跟Java差不多
*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //用kernel_thread方式创建init进程,
//CLONE_FS 子进程与父进程共享相同的文件系统,包括root、当前目录、umask,
//CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表
numa_default_policy(); // 设定NUMA系统的默认内存访问策略
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//用kernel_thread方式创建kthreadd进程,
//CLONE_FILES 子进程与父进程共享相同的文件描述符(file descriptor)表
rcu_read_lock(); //打开RCU读取锁,在此期间无法进行进程切换
/*
* C语言中&的作用是获得变量的内存地址,参考[C指针](http://www.runoob.com/cprogramming/c-pointers.html)
*/
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);// 获取kthreadd的进程描述符,
//期间需要检索进程pid的使用链表,所以要加锁
rcu_read_unlock(); //关闭RCU读取锁
sched_setscheduler_nocheck(kthreadd_task, SCHED_FIFO, ¶m); //设置kthreadd的进程调度策略,
//SCHED_FIFO 实时调度策略,即马上调用,先到先服务,param的优先级之前定义为1
complete(&kthreadd_done); // complete和wait_for_completion是配套的同步机制,跟java的notify和wait差不多,
//之前kernel_init函数调用了wait_for_completion(&kthreadd_done),
//这里调用complete就是通知kernel_init进程kthreadd进程已创建完成,可以继续执行
/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);//current表示当前进程,当前0号进程init_task设置为idle进程
schedule_preempt_disabled(); //0号进程主动请求调度,让出cpu,1号进程kernel_init将会运行,并且禁止抢占
/* Call into cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);// 这个函数会调用cpu_idle_loop()使得idle进程进入自己的事件处理循环
}
init
init进程分为前后两部分,前一部分是在内核启动的,主要是完成创建和内核初始化工作,内容都是跟Linux内核相关的;后一部分是在用户空间启动的,主要完成Android系统的初始化工作。
kernel_init
之前在rest_init函数中启动了init进程,在创建完init进程后,会调用kernel_init函数
/*
* __ref 这个跟之前讲的__init作用一样
*/
static int __ref kernel_init(void *unused)
{
kernel_init_freeable(); //进行init进程的一些初始化操作
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();// 等待所有异步调用执行完成,,在释放内存前,必须完成所有的异步 __init 代码
free_initmem();// 释放所有init.* 段中的内存
mark_rodata_ro(); //arm64空实现
system_state = SYSTEM_RUNNING;// 设置系统状态为运行状态
numa_default_policy(); // 设定NUMA系统的默认内存访问策略
flush_delayed_fput(); // 释放所有延时的struct file结构体
if (ramdisk_execute_command) { //ramdisk_execute_command的值为"/init"
if (!run_init_process(ramdisk_execute_command)) //运行根目录下的init程序
return 0;
pr_err("Failed to execute %s\n", ramdisk_execute_command);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) { //execute_command的值如果有定义就去根目录下找对应的应用程序,然后启动
if (!run_init_process(execute_command))
return 0;
pr_err("Failed to execute %s. Attempting defaults...\n",
execute_command);
}
if (!run_init_process("/sbin/init") || //如果ramdisk_execute_command和execute_command定义的应用程序都没有找到,
//就到根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动
!run_init_process("/etc/init") ||
!run_init_process("/bin/init") ||
!run_init_process("/bin/sh"))
return 0;
panic("No init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
}
其中比较重要的是,在kernel_init_freeable()中,有对驱动的初始化driver_init
那么kernel_init做了以下事情:
- 初始化设备和驱动程序
- 打开标准输入和输出
- 初始化文件系统
完成后会调用run_init_process进入用户空间的init
用户空间init
在用户空间的init中,做了以下事情:
- 创建进程会话密钥并初始化属性系统。
- 进行 SELinux 第二阶段并恢复一些文件安全上下文。
- 新建 epoll 并初始化子进程终止信号处理函数。
- 设置其他系统属性并开启系统属性服务。
- 解析init.rc文件
init.rc文件中会启动servicemanager和zygote
至此,系统的启动过程变为
zygote
zygote 进程就是 daemon 其中之一,zygote 进程主要负责创建 Java 虚拟机,加载系统资源,启动 SystemServer 进程,以及在后续运行过程中启动普通的应用程序。
在init.rc中,有这么一句 import /init.${ro.zygote}.rc ,这就是启动zygote.rc来创建zygote进程了;不同厂家有不同的zygote.rc,并且分为32位和64位。
我们只需要知道,不管是哪个版本,zygote.rc的源文件都是app_main.cpp,并且由于zygote在初始化拉起systemserver之后,会负责普通app的启动,所以在app_main.cpp的结尾是
if (zygote) { // 如果是 zygote 启动模式,则加载 ZygoteInit
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
// 如果是 application 启动模式,则加载 RuntimeInit
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.");
}
我们看到,在最后调用的是 runtime.start 函数,这个就是要启动虚拟机了,接下来我们分析 start 函数。
创建虚拟机
//frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options,
bool zygote) {
// ...
// 打印一些日志,获取 ANDROID_ROOT 环境变量
// ...
/* start the virtual machine */
JniInvocation jni_invocation;
// 初始化JNI,加载 libart.so
jni_invocation.Init(NULL);
JNIEnv* env;
// 创建虚拟机
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
// 表示虚拟创建完成,但是里面是空实现
onVmCreated(env);
/*
* Register android functions.
* 注册 JNI 函数
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
// 接下来的这些语法大家应该比较熟悉了,都是 JNI 里的语法,
// 主要作用就是调用 ZygoteInit 类的 main 函数
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.
*/
// 将 "com.android.internal.os.ZygoteInit"
// 转换为 "com/android/internal/os/ZygoteInit"
char* slashClassName = toSlashClassName(className);
// 找到 class
jclass startClass = env->FindClass(slashClassName);
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");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {// 调用 ZygoteInit.main() 方法
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");
}
CallStaticVoidMethod 反射调用 ZygoteInit 的 main 函数。
zygote初始化完成后,拉起systemserver,就会进入循环,准备启动普通app