文章目录
1、简述
本文简要记录下学习Android系统启动的一些细节,方便自己记忆和宏观的认识。
分析的源码是8.1.0
2、细节概要
总体流程
启动电源 => 拉起引导程序 => linux内核启动 => init进程启动 => zygote进程 => systemServer进程
Q1: init进程是什么?init进程启动?既然init进程这么重要那么init究竟做了什么呢?
- init进程是Android系统中用户空间的第一个进程,进程号是1。
- linux内核启动后会在内核空间创建init进程并在用户空间完成初始化。内核空间创建可查阅文后”参考文章1”。这里我们主要重点研究init进程在用户空间做了什么
1、Android 8.0 系统启动流程之Linux内核启动–kernel_init进程(三) - 功能探究先查看源码后续总结
如下代码选自内核源码,用户空间的init入口函数,主要做了如上所提到的事情。
//system/core/init/init.cpp
int main(int argc, char** argv) {
...
add_environment("PATH", _PATH_DEFPATH);
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
// Clear the umask.
umask(0);
// 挂载文件
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
....
InitKernelLogging(argv);
....
}
...
// 属性服务初始化
property_init();
....
// 启动属性服务
start_property_service();
...
// 默认为true
if (bootscript.empty()) {
// 解析init.rc文件
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
...
return 0;
}
Q2: 属性服务作用特性
可以理解为记录用户以及软件使用信息的注册表工具,即使系统或软件重启依旧可依据属性服务中的记录进行相应的初始化工作。还可以用于软件间信息的获取等。
属性服务的初始化早于zygote进程,写操作采用socket进程来跨进程通信,读操作采用共享内存来读取。
具体如何初始化启动通信可参考文末的2、3。
2、Android 属性服务研究
3、android 开机启动流程分析(04)init启动中关键服务-属性服务
init进程启动总结
- linux 内核阶段进行创建启动,切换到用户空间进行加载
- 创建和挂载所需要要的文件
- 初始化并启动属性服务
- 解析init.rc 脚本文件并创建启动zygote进程
Q3: zygote是什么?zygote进程启动?作用?
zygote进程创建了应用程序进程和SystemServer进程
int main(int argc, char** argv) {
parser.ParseConfig("/init.rc");
}
init.rc是一个非常重要的配置文件,它是由Android初始化语言编写的脚本,共包含5中类型语句(参考4),如下所示。查看下面代码可以发现,会启动classname为main的service,而我们在特定系统的rc文件中发现init新建了一个名为zygote的进程其classname为main,则就代表启动了zygote。
执行do_class_start最终会fork子进程并启动这个进程,那么zygote进程启动起来了。
//system/core/rootdir/init.rc
import /init.environ.rc
import /init.usb.rc
import /init.${
ro.hardware}.rc
import /vendor/etc/init/hw/init.${
ro.hardware}.rc
import /init.usb.configfs.rc
// ro.zygote变量,它可以被理解为一个环境变量
import /init.${
ro.zygote}.rc
// 初始化创建一系列文件
on zygote-start && property:ro.crypto.state=unencrypted
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on nonencrypted
class_start main
class_start late_start
....
ro.zygote变量,它可以被理解为一个环境变量,这里我们看64里面的。
init进程会创建名为zygote的进程,程序的执行路径为
/system/bin/app_process64,zygote的classname为main
//system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
app_process关联到app_main中,由于zygote进程包含有zygote则runtime会进入分之一中,进入具体的start方法这里会解析字符串和args最终会jni调用com/android/internal/os/ZygoteInit 中的main函数
//frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]){
...
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.");
}
}
我们看看AndroidRumtime具体做了哪些操作
//frameworks/base/core/jni/AndroidRuntime.cpp
/*
* 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) {
return;
}
onVmCreated(env);
/*
* 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);
}
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);