安卓app_process启动进程的底层流程

前言

app_process可是个好东西,由它启的进程虽然也是运行在手机上,但却是独立运行在linux上的,不受android的管控,所以我们可以利用framework.jar做很多事情。

1.源码位置&编译

源码位于 frameworks/base/cmds/app_process/app_main.cpp

相关源码位置

frameworks/base/core/jni/AndroidRuntime.cpp

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

编译:source, lunch完在以上目录mma 或者 croot , make app_process

2.简要启动流程

2.1 主线-初识AppRuntime

android的根进程zygote还有system_server就是app_process启动的,这里主要写一下tool这个方向的启动流程

输入app_process的启动命令后,会执行app_mainmain方法。

2.1.1 AppRuntime的创建

main方法首先创建了AppRuntime的对象,AppRuntimeAndroidRuntime的子类

int main(int argc, char* const argv[])
{
...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...

2.1.2 AppRuntime类

AppRuntime有以下几个方法,tool的启动流程里 我们主要关注setClassNameAndArgsonVmCreatedonStarted这三个方法,这里先简单的列一下该类和方法。

class AppRuntime : public AndroidRuntime
{

func 1.

void setClassNameAndArgs(const String8& className, int argc, char * const *argv)
{
...
}

func 2.

virtual void onVmCreated(JNIEnv* env)
{
...
}

func 3.

virtual void onStarted()
{
...
}

func 4.

virtual void onZygoteInit()
{
...
}

func5.

virtual void onExit(int code)
{
...
}

2.1.3 父类构造

这时候会走到AndroidRuntime的构造, init_android_graphics会初始化android的UI图形化,然后赋值gCurRuntime为当前类对象。这里后面会用到

AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
        mExitWithoutCleanup(false),
        mArgBlockStart(argBlockStart),
        mArgBlockLength(argBlockLength)
{
    init_android_graphics();

    // Pre-allocate enough space to hold a fair number of options.
    mOptions.setCapacity(20);

    assert(gCurRuntime == NULL);        // one per process
    gCurRuntime = this;
}

 

2.2 支线-skia的初始化

Skia是google的图形化的库,android,chrome,flutter的渲染都离不开它。

所以这里浅浅的跟一下。首先看init_android_graphics

我们可以看见引入了 android/graphics/jni_runtime.h这个头文件。

void init_android_graphics() {
    SkGraphics::Init();
}

这里导入了SkGraphics.h并调用了SkGraphicsInit方法。

SkGraphics.h位于external/skia/include/core/SkGraphics.h

这里真正调用到的是external/skia/src/core/SkGraphics.cppInit方法

void SkGraphics::Init() {
    // SkGraphics::Init() must be thread-safe and idempotent.
    SkCpu::CacheRuntimeFeatures();
    SkOpts::Init();
}

第一个方法会去读cpu的信息 这里是cpuid相关的文档https://www.sandpile.org/x86/cpuid.htm

 static uint32_t read_cpu_features() {
        uint32_t features = 0;
        uint32_t abcd[4] = {0,0,0,0};

        cpuid(abcd);
        if (abcd[3] & (1<<25)) { features |= SkCpu:: SSE1; }
        if (abcd[3] & (1<<26)) { features |= SkCpu:: SSE2; }
        if (abcd[2] & (1<< 0)) { features |= SkCpu:: SSE3; }
        if (abcd[2] & (1<< 9)) { features |= SkCpu::SSSE3; }
        if (abcd[2] & (1<<19)) { features |= SkCpu::SSE41; }
        if (abcd[2] & (1<<20)) { features |= SkCpu::SSE42; }

        if ((abcd[2] & (3<<26)) == (3<<26)         // XSAVE + OSXSAVE
             && (xgetbv(0) & (3<<1)) == (3<<1)) {  // XMM and YMM state enabled.
            if (abcd[2] & (1<<28)) { features |= SkCpu:: AVX; }
            if (abcd[2] & (1<<29)) { features |= SkCpu::F16C; }
            if (abcd[2] & (1<<12)) { features |= SkCpu:: FMA; }

            cpuid7(abcd);
            if (abcd[1] & (1<<5)) { features |= SkCpu::AVX2; }
            if (abcd[1] & (1<<3)) { features |= SkCpu::BMI1; }
            if (abcd[1] & (1<<8)) { features |= SkCpu::BMI2; }
            if (abcd[1] & (1<<9)) { features |= SkCpu::ERMS; }

            if ((xgetbv(0) & (7<<5)) == (7<<5)) {  // All ZMM state bits enabled too.
                if (abcd[1] & (1<<16)) { features |= SkCpu::AVX512F; }
                if (abcd[1] & (1<<17)) { features |= SkCpu::AVX512DQ; }
                if (abcd[1] & (1<<21)) { features |= SkCpu::AVX512IFMA; }
                if (abcd[1] & (1<<26)) { features |= SkCpu::AVX512PF; }
                if (abcd[1] & (1<<27)) { features |= SkCpu::AVX512ER; }
                if (abcd[1] & (1<<28)) { features |= SkCpu::AVX512CD; }
                if (abcd[1] & (1<<30)) { features |= SkCpu::AVX512BW; }
                if (abcd[1] & (1<<31)) { features |= SkCpu::AVX512VL; }
            }
        }
        return features;
    }

这里就不细跟这条支线了,感兴趣的可以按对应路径继续跟一下。

 

2.3 主线-Jvm参数设置

紧接着在main函数对参数做了判断和处理 首先是jvm的参数判断和传递

  const char* spaced_commands[] = { "-cp", "-classpath" };
    // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
    bool known_command = false;

    int i;
    for (i = 0; i < argc; i++) {
        if (known_command == true) {
          runtime.addOption(strdup(argv[i]));
          // The static analyzer gets upset that we don't ever free the above
          // string. Since the allocation is from main, leaking it doesn't seem
          // problematic. NOLINTNEXTLINE
          ALOGV("app_process main add known option '%s'", argv[i]);
          known_command = false;
          continue;
        }

        for (int j = 0;
             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
             ++j) {
          if (strcmp(argv[i], spaced_commands[j]) == 0) {
            known_command = true;
            ALOGV("app_process main found known command '%s'", argv[i]);
          }
        }

        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i]));
        // The static analyzer gets upset that we don't ever free the above
        // string. Since the allocation is from main, leaking it doesn't seem
        // problematic. NOLINTNEXTLINE
        ALOGV("app_process main add option '%s'", argv[i]);
    }  

runtime.addOptionAndroidRuntime的方法 会把vm相关的参数放进Vector然后传给vm

void AndroidRuntime::addOption(const char* optionString, void* extraInfo)
{
    JavaVMOption opt; 
    opt.optionString = optionString;
    opt.extraInfo = extraInfo;
    mOptions.add(opt);
}

这里调用了mOptions.add,mOptions是定义在AndroidRuntime.h里的。

Vector<JavaVMOption> mOptions;

2.4 主线-zygote,system_server的判断

Zygote , SystemServer都是app_process启动的。

对于其他参数 这里主要针对启动的类型做了以下判断

    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 = (arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className = arg;
            break;
        } else {
            --i;
            break;
        }
    }

2.5 主线-其他判断

这部分代码主要是其他判断,参数的整合以及进程名的设置

  Vector<String8> args;
    if (!className.empty()) {
        // We're not in zygote mode, the only argument we need to pass
        // to RuntimeInit is the application argument.
        //
        // The Remainder of args get passed to startup class main(). Make
        // copies of them before we overwrite them with the process name.
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);

        if (!LOG_NDEBUG) {
          String8 restOfArgs;
          char* const* argv_new = argv + i;
          int argc_new = argc - i;
          for (int k = 0; k < argc_new; ++k) {
            restOfArgs.append("\"");
            restOfArgs.append(argv_new[k]);
            restOfArgs.append("\" ");
          }
          ALOGV("Class name = %s, args = %s", className.c_str(), restOfArgs.c_str());
        }
    } 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.empty()) {
        runtime.setArgv0(niceName.c_str(), true /* setProcName */);
    }

2.6 进程的启动

2.6.1 启动的入口

main方法里的最后会对zygote和非zygote做不同的启动 并把刚才的参数扔进去

if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (!className.empty()) {
        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.");
    }   

2.6.2 AndroidRuntimestart方法

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{

***

onVmCreated(env);

***

onVmCreated(env);

***

    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).c_str());
        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 != 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);

}

这里主要做了两件事

2.6.2.1 AppRuntime onVmCreated

这里会调用onVmCreated,父类这里是空实现,会走到AppRuntimeonVmCreated

virtual void onVmCreated(JNIEnv* env)
    {   
        if (mClassName.empty()) {
            return; // Zygote. Nothing to do here.
        }

        /*
         * This is a little awkward because the JNI FindClass call uses the
         * class loader associated with the native method we're executing in.
         * If called in onStarted (from RuntimeInit.finishInit because we're
         * launching "am", for example), FindClass would see that we're calling
         * from a boot class' native method, and so wouldn't look for the class
         * we're trying to look up in CLASSPATH. Unfortunately it needs to,
         * because the "am" classes are not boot classes.
         *
         * The easiest fix is to call FindClass here, early on before we start
         * executing boot class Java code and thereby deny ourselves access to
         * non-boot classes.
         */
        char* slashClassName = toSlashClassName(mClassName.c_str());
        mClass = env->FindClass(slashClassName);
        if (mClass == NULL) {
            ALOGE("ERROR: could not find class '%s'\n", mClassName.c_str());
        }
        free(slashClassName);

        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
    }

这里主要就是通过JNIEnv的指针拿到对应的进程入口字节码对象,并赋值给AppRuntime的成员mClass

最后在启动的时候要用到该成员

2.6.3 RuntimeInit的处理

入口会调用AndroidRuntimestart方法 启动RuntimeInit. 下面是RuntimeInitmain方法

public static final void main(String[] argv) {
        preForkInit();
        if (argv.length == 2 && argv[1].equals("application")) {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
            redirectLogStreams();
        } else {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
        }

        commonInit();

        /*
         * Now that we're running in interpreted code, call back into native code
         * to run the system.
         */
        nativeFinishInit();

        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
    }   

preForkInit主要做了启动前的准备工作

如果这里是你的进程并未传入--application 所以不会走redirectLogStreams的逻辑,实际redirectLogStreams也只是做了一些日志相关的处理。

这时候会调用到commonInit,然后jni调用nativeFinishInit

下面是commonInit方法

 protected static final void commonInit() {
        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");

        /*
         * set handlers; these apply to all threads in the VM. Apps can replace
         * the default handler, but not the pre handler.
         */
        LoggingHandler loggingHandler = new LoggingHandler();
        RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));

        /*
         * Install a time zone supplier that uses the Android persistent time zone system property.
         */
        RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));

        /*
         * Sets handler for java.util.logging to use Android log facilities.
         * The odd "new instance-and-then-throw-away" is a mirror of how
         * the "java.util.logging.config.class" system property works. We
         * can't use the system property here since the logger has almost
         * certainly already been initialized.
         */
        LogManager.getLogManager().reset();
        new AndroidConfig();

        /*
         * Sets the default HTTP User-Agent used by HttpURLConnection.
         */
        String userAgent = getDefaultUserAgent();
        System.setProperty("http.agent", userAgent);

        /*
         * Wire socket tagging to traffic stats.
         */
        TrafficStats.attachSocketTagger();

        initialized = true;
    }

做了异常处理,AndroidConfig(其实还是log相关)等等。

2.6.4 回归AndroidRuntime

nativeFinishInitjni调用到AndroidRuntime的方法

tatic void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onStarted();
}

可以回看2.1.3的最后标粗的代码,这里的gCurRuntime就是this,也就是在main方法里声明的AppRuntime的类对象

2.6.5 AppRuntime onStart

所以来看一下AppRuntime重写的onStart方法

  virtual void onStarted()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();

        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        ar->callMain(mClassName, mClass, mArgs);

        IPCThreadState::self()->stopProcess();
        hardware::IPCThreadState::self()->stopProcess();
    }

2.6.5.1 startThreadPool

这里主要是调用了ProcessState的方法,这里主要是创建了一个binder线程。详细的代码在frameworks/native/libs/binder/ProcessState.cpp

void ProcessState::startThreadPool()
{
    std::unique_lock<std::mutex> _l(mLock);
    if (!mThreadPoolStarted) {
        if (mMaxThreads == 0) {
            // see also getThreadPoolMaxTotalThreadCount
            ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
                  "*startThreadPool when zero threads are requested.");
        }
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }   
}

2.6.5.2 真正的进程启动

这里接着调用到了父类AndroidRuntimecallMain方法,传入onVmCreated里获取的进程入口字节码对象

status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
    const Vector<String8>& args)
{
    JNIEnv* env; 
    jmethodID methodId;

    ALOGD("Calling main entry %s", className.c_str());

    env = getJNIEnv();
    if (clazz == NULL || env == NULL) {
        return UNKNOWN_ERROR;
    }    

    methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
    if (methodId == NULL) {
        ALOGE("ERROR: could not find method %s.main(String[])\n", className.c_str());
        return UNKNOWN_ERROR;
    }    

    /*   
     * We want to call main() with a String array with our arguments in it.
     * Create an array and populate it.
     */
    jclass stringClass;
    jobjectArray strArray;

    const size_t numArgs = args.size();
    stringClass = env->FindClass("java/lang/String");
    strArray = env->NewObjectArray(numArgs, stringClass, NULL);

    for (size_t i = 0; i < numArgs; i++) {
        jstring argStr = env->NewStringUTF(args[i].c_str());
        env->SetObjectArrayElement(strArray, i, argStr);
    }    

    env->CallStaticVoidMethod(clazz, methodId, strArray);
    return NO_ERROR;
}

这里就是真正启动进程的地方,也很简单,顺序就是先获取JNIEnv的指针,然后根据在vmCreated里赋值的mClass成员找到对应进程的main方法,传入参数,启动main方法。

至此,进程就被真正的调用起来了。

 

如有疏漏欢迎指正,后续会跟进并补出app_process启动zygote的流程

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值