从pm命令执行细说AndroidRuntime的启动初始化

####从pm命令执行细说AndroidRuntime启动 我们在PC端执行adb shell pm package list时,其实通过adb通信最终执行的是system/bin目录下的pm脚本。pm脚本内容如下

# Script to start "pm" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/pm.jar
exec app_process $base/bin com.android.commands.pm.Pm "$@"

下面我们就从这个脚本展开 ####脚本本身 这个脚本前面两行主要是设置执行环境变量,也就是虚拟机环境,和各个OSPATH是一个意思。这个脚本的重点在第三句exec app_process $base/bin com.android.commands.pm.Pm "$@"也就是执行app_process并且将后面的一串作为参数传入。其中“$@”是执行shell脚本时传入的参数。而app_process是一个可执行文件,位于/system/bin目录下。 ####app_process app_process入口源码是app_main.cpp。在main函数中首先会初始化一个AppRuntimeAppRuntime其实就是一个AndroidRuntime.下面就是main函数主体

int main(int argc, char* const argv[])
{
   // ....
    // These are global variables in ProcessState.cpp
    mArgC = argc;
    mArgV = argv;
    mArgLen = 0;
    for (int i=0; i<argc; i++) {
        mArgLen += strlen(argv[i]) + 1;
    }
    mArgLen--;
    AppRuntime runtime;   //初始化一个AppRuntime
    const char* argv0 = argv[0];

    // Process command line arguments
    // ignore argv[0]  argv[0]在这里就是app_process
    argc--;
    argv++;
    // Everything up to '--' or first non '-' arg goes to the vm
    int i = runtime.addVmArguments(argc, argv); //将参数直接放入运行时环境
    // Parse runtime arguments.  Stop at first unrecognized option.
    //从下面的解析可以看出来在app_process后面可以带一些必须--开头的虚拟机参数带的参数主要是
    //--zygote表示启动Zygote进程,--start-system-server系统服务,--application应用,--nice-name进程名称。然后没有--第一个参数是parentDir,对应到pm启动脚本就是/system/bin.第二个从参数是虚拟机要启动的包名也就是com.android.commands.pm.Pm
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    const char* parentDir = NULL;
    const char* niceName = NULL;
    const char* className = NULL;
    while (i < argc) {
        const char* arg = argv[i++];
        if (!parentDir) {
            parentDir = arg;
        } else if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = "zygote";
        } 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 {
            className = arg;
            break;
        }
    }
    if (niceName && *niceName) {
        setArgv0(argv0, niceName);
        set_process_name(niceName);
    }
    runtime.mParentDir = parentDir;//目前没发现这个目录干什么用
    //根据解析来决定启动的进程是什么。对于pm当然是走 else if分支。
    //请留意,在这里启动了另外一个进程虚拟机进程com.android.internal.os.RuntimeInit.
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // Remainder of args get passed to startup class main()
        runtime.mClassName = className;
        runtime.mArgC = argc - i;
        runtime.mArgV = argv + i;
        runtime.start("com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}

####AndroidRuntime 从上面的代码来看,这里主要是初始化AppRuntime。然后启动com.android.internal.os.RuntimeInit虚拟机进程,其实就是Android进程的孵化器,当然称作初始化Android运行时更恰当。启动的代码如下。

void AndroidRuntime::start(const char* className, const char* options)
{

	//.....
    //这里虚拟机
    //....
    //对参数进行判断,决定启动什么进程
    //.....
    //下面就是初始化并启动Java虚拟机了
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    onVmCreated(env);
   	//讲与android相关的JNI接口注册到Java虚拟机。方便孵化器启动Android进程。因为孵化器需要使用到Android自己需要的JNI接口。这里科一看下startReg实现,我就不详述了。里面主要使用到了一个gRegJNI数组,这个数组是一个函数指针,和每个android底层需要注册JNI的类对应。通过循环这个数组执行指针函数进行注册。
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
	//这个下面就是启动孵化器ZygoteInit或是RuntimeInit.具体参考app_main.cpp中main函数结尾启动部分。
    //....
    //准备孵化器启动参数
    //....
	//将孵化器class的点转化成反斜杠
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        //在类中寻找main方法
        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 {
        //执行main方法。当然针对pm流程来看,这里就是启动RuntimeInit的main方法。
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
    }
    free(slashClassName);
   //....
}

在这里必须要理一下AppRuntimeAndroidRuntime的关系。uml类图关系如下 AppRuntime和AndroidRuntime关系 从图看以看出AppRuntime继承了AndroidRuntime。并且实现了AndroidRuntime的一些方法,当然在这里主要需要关注下那几个回调函数,也就是on开头的函数,也就是onVmCreated onStarted onZygoteInit onExit特别是onStarted是纯虚函数。其他几个都是空实现。上面说到了启动了RuntimeInit进程。那这个进程在干什么呢? ####RuntimeInit 这里还是先梳理下这个类结构 RuntimeInit 其中有三个本地方法就是nativeZygoteInit nativeFinishInit ativeSetExitWithoutCleanup。通过前面的介绍其实前两个函数在这个启动流程中意义是一样的,只是在不同分支或是根据启动的进程不一样来执行不一样的函数。这几个函数注册在上面介绍过的startReg函数中完成,对应的回调函数是register_com_android_internal_os_RuntimeInit

static JNINativeMethod gMethods[] = {
    { "nativeFinishInit", "()V",
        (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
    { "nativeZygoteInit", "()V",
        (void*) com_android_internal_os_RuntimeInit_nativeZygoteInit },
    { "nativeSetExitWithoutCleanup", "(Z)V",
        (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
};

int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
    return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
        gMethods, NELEM(gMethods));
}

那RuntimeInit的main函数在做什么呢,这个在RuntimeInit.java件中。

public static final void main(String[] argv) {
		//这个参数还记得吗,是在app_main的runtime.start时候传过来的
        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();
        nativeFinishInit();
}

这里就做一件事,通用初始化commonInit,我暂时还不知道这在干么,如果有知道的可以告诉我。然后执行nativeFinishInit。这个根据上面的注册可知其实调用AndroidRuntimecom_android_internal_os_RuntimeInit_nativeFinishInit方法。这个方法调用AndroidRuntime 的 onSatrted方法,也就是调回runtime实例。然后回AppRuntimeonstarted,这个gCurRuntime是在构造runtime是创建的,其实就是保存的一个runtime的指针

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

那么在onStarted中在做什么呢,这是个回调函数

 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, mArgC, mArgV);
        IPCThreadState::self()->stopProcess();
 }

这里的mClassName是在app_main解析参数的时候保存的。也就是这个实例中的android.commands.pm.PmmClass就是注册虚拟机后的实例,在onVmCreated中绑定。通过callMain从而调用pm,这些参数随之传入Pmmain函数,从而启动了pm list package功能。pm的代码实现在Pm.java中。在这里运行时启动分析完成了。至于pm如何实现的请自行学习。下面来一张总体调用时序图 Invoke_squence


猜你喜欢

转载自blog.csdn.net/weixin_38503885/article/details/80751348