首页 > 其他分享 >安卓系统启动流程解析

安卓系统启动流程解析

时间:2024-09-15 13:45:48浏览次数:1  
标签:main 系统启动 安卓 system argv start init zygote 解析

安卓系统启动流程

目录

光看分析文章还是不够的,还是要和实践结合。

1 init阶段

init 命令的入口是init/main.cppmain()

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#elif __has_feature(hwaddress_sanitizer)
    __hwasan_set_error_report_callback(AsanReportCallback);
#endif

// //以上部分是和编译相关的语法,满足条件时则保留后面的语句。它具体针对的是两种不同的内存检查工具:AddressSanitizer (ASan) 和 Hardware Address Sanitizer (HWAsan)。

    // Boost prio which will be restored later
    // 提升权限,后续会恢复
    setpriority(PRIO_PROCESS, 0, -20);
    if (!strcmp(basename(argv[0]), "ueventd")) {
        //init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        // 根据不同参数执行不同阶段
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }
    // 最开始执行第一阶段
    return FirstStageMain(argc, argv);
}

init的启动阶段图

1.1 FirstStage

firstStage的源码在/system/core/init/first_stage_init.cpp

大部分是创建启动所需的各种目录,还初始化kernel log,最下面有这么一段

     const char* path = "/system/bin/init"; 
     const char* args[] = {path, "selinux_setup", nullptr}; 
    auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    close(fd);
     execv(path, const_cast<char**>(args)); 

    // execv() only returns if an error happened, in which case we
    // panic and never fall through this conditional.
    PLOG(FATAL) << "execv(\"" << path << "\") failed";

    return 1;
}

这就是进入下一阶段的代码

1.2 SELinux Setup

SetupSelinux主要功能就是管理selinux和相关策略。

SELinux(Security Enhanced Linux)是一种为Linux内核提供的强制访问控制(MAC, Mandatory Access Control)框架。它的设计目的是为了提高Linux系统的安全性,通过严格的权限管理机制来限制进程和用户的访问权限,从而减少系统被攻击的风险。

在SELinux Setup的结尾也有进入下一阶段的代码。

1.3 SecondStage

在SecondStage中有一个LoadBootScripts的函数

init.rc是和init相关的配置文件

这里会在 LoadBootScripts() 方法中解析 init.rc 文件。关于该文件指令的含义可以参考 AOSP 中的文档:《Android Init Language》. 完成解析相关的类是 ActionManagerParserXXParser,均位于 system/core/init 目录下面。除此之外,还有 ActionService 等类。它们的作用是,各种 Parser 用来解析 rc 文件中的指令。解析出的指令会被封装成 ActionService 等对象。
作者:开发者如是说
链接:https://juejin.cn/post/6844904137268674568
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

简单摘要一下SecondStage的代码

int SecondStageMain(int argc, char** argv) {
    ...
    ActionManager& am = ActionManager::GetInstance();
    ...
     am.QueueEventTrigger("early-init"); 

    // Trigger all the boot actions to get us started.
     am.QueueEventTrigger("init"); 
    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
         am.QueueEventTrigger("charger"); 
    } else {
         am.QueueEventTrigger("late-init"); 
    }
    ...
}

这里是构造了一个EventTrigger对象, 放到事件队列中,但是并没有触发

直到ExecuteOneCommand才会依次触发

具体做什么,就在读取的配置文件中。源代码中位于/system/core/rootdir/init.rc

init中会start service manager

在lateinit中会trigger zygote-start

zygote-start会根据不同的配置,执行不同的操作,但最终都会执行start zygote

2 init.rc的配置

上面提到在init.rc中有start zygote等启动服务的配置,那么这个start又做了什么。

根据https://chromium.googlesource.com/aosp/platform/system/core/+/refs/heads/master/init

这里的解释还是只有启动服务。

这里的 start 会被映射到 builtins 类的 do_start() 方法。该方法会调用 Servicestart() 方法。该方法主要是调用 clonefork 创建子进程,然后调用 execve 执行配置的二进制文件,另外根据之前在 rc 文件中的配置,去执行这些配置。
作者:开发者如是说
链接:https://juejin.cn/post/6844904137268674568
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

builtinssystem/core/init/builtins.cpp

system/core/init/service.cpp

servicemanager的配置位于/framework/native/cmds/servicemanager

zygote的配置是根据系统类型变化的,在init.rc的开头被import

zygote64为例,配置文件如下

主要是启动了/system/bin/app_process64

相关源码在/frameworks/base/cmds/app_process/

3 Zygote的启动

3.1 app_process

/frameworks/base/cmds/app_process/app_main.cpp

主要是进行参数解析

还有启动ZygoteInit

这里的runtimeAndroidRuntime

AndroidRuntime定义于/frameworks/base/core/jni/AndroidRuntime.cpp

所以这里的 start() 方法是一种 JNI (Java Native Interface)调用。这里将会调用 Java 中的静态 main() 方法继续执行。这里是在 C++ 中调用 Java 的方法。

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ...
    
    /* 获取环境变量 */
    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /system does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    ...

    /* start the virtual machine */
    /* startVM启动虚拟机 */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);

    ...
    
    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
     /* 获取类及其Main,并Call */
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    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 {
            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");
}


3.2 Zygoteinit.java

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String[] argv) {
    ZygoteServer zygoteServer = null;

    // Mark zygote start. This ensures that thread creation will throw
    // an error.
    ZygoteHooks.startZygoteNoThreadCreation();

    // Zygote goes into its own process group.
    // 设置进程权限
    try {
        Os.setpgid(0, 0);
    } catch (ErrnoException ex) {
        throw new RuntimeException("Failed to setpgid(0,0)", ex);
    }

    Runnable caller;
    try {
        // Store now for StatsLogging later.
        final long startTime = SystemClock.elapsedRealtime();
        final boolean isRuntimeRestarted = "1".equals(
                SystemProperties.get("sys.boot_completed"));

        String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
        TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
                Trace.TRACE_TAG_DALVIK);
        bootTimingsTraceLog.traceBegin("ZygoteInit");
        RuntimeInit.preForkInit();

        boolean startSystemServer = false;
        String zygoteSocketName = "zygote";
        String abiList = null;
        boolean enableLazyPreload = false;
        
        // 解析参数
        for (int i = 1; i < argv.length; i++) {
            if ("start-system-server".equals(argv[i])) {
                startSystemServer = true;
            } else if ("--enable-lazy-preload".equals(argv[i])) {
                enableLazyPreload = true;
            } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                abiList = argv[i].substring(ABI_LIST_ARG.length());
            } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
            } else {
                throw new RuntimeException("Unknown command line argument: " + argv[i]);
            }
        }

        // ...  资源预加载与日志
        
         // 启动zygoteServer 
         zygoteServer = new ZygoteServer(isPrimaryZygote); 

        if (startSystemServer) {
             // 启动SystemServer 
             Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
 
            // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
            // child (system_server) process.
            if (r != null) {
                r.run();
                return;
            }
        }

        Log.i(TAG, "Accepting command socket connections");

        // The select loop returns early in the child process after a fork and
        // loops forever in the zygote.
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with fatal exception", ex);
        throw ex;
    } finally {
        if (zygoteServer != null) {
            zygoteServer.closeServerSocket();
        }
    }

    // ...
}

这里主要做了

  • 创建ZygoteServer 对象。提供了等待 UNIX 套接字的命令,并且提供了 fork 虚拟机的方法。
  • 资源预加载
  • 调用 forkSystemServer() 。其中会调用 Zygote 的静态方法来 Fork 一个子进程。该方法内部又会调用 JNI 层的 nativeForkSystemServer 方法最终完成 Fork 操作。
  • 启动 select 循环,等待连接。会等待接受命令,fork子进程,并返回子进程的 main 方法

4 SystemServer

启动了SystemServer后,SystemServer还会拉起其他的系统内服务。

/frameworks/base/services/java/com/android/server/SystemServer.java

摘要一些代码分析

// frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
    new SystemServer().run();
}

// frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {
    TimingsTraceAndSlog t = new TimingsTraceAndSlog();
    try {

        // ...

        // Create the system service manager.
         // 启动system service manager 
        mSystemServiceManager = new SystemServiceManager(mSystemContext);
        mSystemServiceManager.setStartInfo(mRuntimeRestart,
                mRuntimeStartElapsedTime, mRuntimeStartUptime);
        mDumper.addDumpable(mSystemServiceManager);

        
    // Start services.
    try {
        t.traceBegin("StartServices");
        startBootstrapServices(t);   // 启动引导服务 
        startCoreServices(t);     // 启动核心服务 
        startOtherServices(t);     // 启动其他服务 
        startApexServices(t);     // 启动Apex服务 
    } catch (Throwable ex) {
        Slog.e("System", "******************************************");
        Slog.e("System", "************ Failure starting system services", ex);
        throw ex;
    } finally {
        t.traceEnd(); // StartServices
    }

    // ...
    
    // Loop forever.
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}


各种启动服务的操作是通过调用 SystemServiceManagerstartService()

startBootstrapServices() 中会拉起Activity Manager ServicePackage Manager Service

Apex服务是指Android操作系统中的一种应用程序启动方式,它允许应用程序在设备启动时以系统服务的形式自动运行。这些服务通常包括系统应用、框架服务和系统UI等。它们在设备启动时会自动运行,并为用户提供各种基础功能和界面。
startApexServices方法会遍历所有已安装的Apex服务,并调用它们的启动方法,使它们在系统启动时自动运行。该方法在系统启动过程中被调用,是Android操作系统启动过程中的一部分。

5 总结

6 引用

Android 系统启动.md
Android 系统启动流程 | 皮皮小黑屋 Android 系统启动流程分析
Android 系统启动过程源码分析 - 掘金社区
Android 12 S 系统开机流程分析 - SetupSelinux(二)-CSDN博客

标签:main,系统启动,安卓,system,argv,start,init,zygote,解析
From: https://www.cnblogs.com/Joooook/p/18415190

相关文章

  • 安卓架构
    安卓架构目录1Linux内核层2硬件抽象层HAL3NativeC/C++库&&AndroidRuntime4JavaFramework层5SystemApps层1Linux内核层Android平台的基础是Linux内核。例如,ART依靠Linux内核来执行底层功能。Linux内核的安全机制为Android提供了相应的保障,也......
  • SQL查询技巧:深入解析学生选课系统数据库
            在大学的学生选课系统中,数据库的管理和查询是日常操作中的重要部分。本文通过一系列具体的SQL查询示例,深入解析如何高效地从数据库中获取所需信息,包括学生选课情况、成绩分析、教师课程管理等。系统数据库结构首先,我们有一个包含以下表的数据库:course -存......
  • FastAPI 应用安全加固:HTTPSRedirectMiddleware 中间件全解析
    在当今的网络环境中,数据安全变得越来越重要。HTTPS作为一种安全协议,它通过加密传输数据来保护用户信息免受窃取和篡改。在FastAPI应用中,确保所有的HTTP请求都通过HTTPS进行是至关重要的。中间件在FastAPI中用于处理请求前后的通用任务,例如身份验证、日志记录、请......
  • cJSON-轻量级解析模块、字符串的神——编织STM32C8T6与阿里云信息传递的纽带
            编写方向:本人就不泛泛的编写一篇什么一文学会cJSON了,没什么突出点,也就我水水字数,你们看来看去也不懂,本人是从上阿里云传信息接触的cJSON的,我就此写一篇针对性的文章,希望对大家有用,后期我在其他方面用到还会继续更新。一、简介        cJSON是一个用C......
  • Javaweb之SpringBootWeb案例之阿里云OSS服务的详细解析
     2.3阿里云OSS2.3.1准备阿里云是阿里巴巴集团旗下全球领先的云计算公司,也是国内最大的云服务提供商。编辑云服务指的就是通过互联网对外提供的各种各样的服务,比如像:语音服务、短信服务、邮件服务、视频直播服务、文字识别服务、对象存储服务等等。当我们在项目开发时需要用到某......
  • 前端实现DNS解析和优化
            作为前端开发者如何针对DNS做优化?众所周知,这个DNS是做域名解析的,输入一个域名,经过DNS解析之后就可以得到一个IP地址,互联网中做请求都要把域名转成IP,这个转换过程是比较复杂的,就比较耗时,所以说有优化的空间,它转换之后是会做本地缓存。所以我们优化的目标主要是......
  • 高级 Python Web 应用中的身份验证与授权机制解析
    高级PythonWeb应用中的身份验证与授权机制解析目录......
  • 【Ehviewer绿色版】1.9.8.4最新版本下载2024安卓苹果
     Ehviewer是一款主要用于浏览和下载漫画、插画等二次元图像内容的软件。适用安卓和苹果系统,Ehviewer拥有海量的漫画作品,涵盖各种题材和风格,包括日本漫画、韩国漫画、欧美漫画以及国内的一些同人创作等。无论是热门的商业漫画还是小众的独立作品,都能在Ehviewer上找到,现在已经更......
  • 数据资产入表全流程解析,助力企业数据要素价值释放
    数据资产入表即数据资产会计核算,指的是把有价值的数据编制进资产负债表,作为企业沉淀的无形资产,让数据要素的交易流通变得合规,数据价值可计算。2023年8月21日,财政部发布《企业数据资源相关会计处理暂行规定》,并于2024年1月1日开始实施,首次将数据资源纳入企业会计核算体系,明确了数据......
  • 2024年06月中国电子学会青少年软件编程(图形化)等级考试试卷(四级)答案 + 解析
    青少年软件编程(图形化)等级考试试卷(四级)分数:100题数:24一、单选题(共10题,共30分)1.运行下列程序,输入单词“PLAY”,最后角色说?()A.LY4APB.AP4LYC.YA4PLD.PL4AY正确答案:B答案解析:根据程序分析可知,首先获取单词字符数,然后奇数位的字母放在字符数左侧,......