Android 系统启动分析

1、简述

本文简要记录下学习Android系统启动的一些细节,方便自己记忆和宏观的认识。
分析的源码是8.1.0

2、细节概要
总体流程

启动电源 => 拉起引导程序 => linux内核启动 => init进程启动 => zygote进程 => systemServer进程

Q1: init进程是什么?init进程启动?既然init进程这么重要那么init究竟做了什么呢?
  1. init进程是Android系统中用户空间的第一个进程,进程号是1。
  2. linux内核启动后会在内核空间创建init进程并在用户空间完成初始化。内核空间创建可查阅文后”参考文章1”。这里我们主要重点研究init进程在用户空间做了什么
    1、Android 8.0 系统启动流程之Linux内核启动–kernel_init进程(三)
  3. 功能探究先查看源码后续总结
    如下代码选自内核源码,用户空间的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进程启动总结
  1. linux 内核阶段进行创建启动,切换到用户空间进行加载
  2. 创建和挂载所需要要的文件
  3. 初始化并启动属性服务
  4. 解析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);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值