안드로이드/안드로이드 프레임워크

안드로이드에서 Zygote가 실행되는 순서 1

냥냥냥냥냥냥 2022. 5. 13. 01:25

안녕하세요 개발자 도도입니다

이번엔 Zygote 실행 순서에 대해서 알아볼까 합니다

저도 공부하면서 쓰는 내용이다 보니 틀린 내용이나 잘못 알고 있는 부분이 있을 수 있으니

댓글이나 쪽지로 알려주시면 수정하도록 하겠습니다 ㅎ

 

먼저, 안드로이드 운영체제는 리눅스 운영체제 기반위에서 동작을 하게 됩니다

따라서 리눅스와 동일하게 어플리케이션의 실행은 프로세스로 관리가 되지만 

안드로이드에서는 좀 더 빠르게 어플리케이션을 실행 하기 위해 Zygote라는 것을 사용합니다

 

그럼 먼저 Zygote가 어디서 시작이 되는지 부터 알아봅시다

(init 부분에 대한 분석은 추후 하고 여기서는 init.zygote.rc 파일에서 그냥 README 문법 부분 설명대로 시작을 합니다)

 

 

Init.zygote32.rc or Init.zygote64.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart media.tuner
    onrestart restart netd
    onrestart restart wificond
    task_profiles ProcessCapacityHigh
    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

32 bit나 64 bit의 경우 각각 해당하는 init.zygote(bit).rc 파일에서 실행이 될 것입니다

 

 

여기서 아래 문장을 주목하면 됩니다

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

 

 

사실 .rc 파일의 문법은 제가 잘 모르는 부분이기도 해서 README.md (core/init/README.md) 파일 참고 부탁드립니다)

여기서 service 관련 부분은 아래와 같이 적혀 있습니다 (README.md 파일에서 발췌)

Services
--------
Services are programs which init launches and (optionally) restarts
when they exit.  Services take the form of:

    service <name> <pathname> [ <argument> ]*
       <option>
       <option>
       ...

 

즉 해석을 해보면 /system/bin/app_process path 부분의 zygote를 실행한다라고 보면 될 것 같습니다
그럼 /system/bin/app_process부분도 알아야 겠죠?

 

 

 

 

 

 

Android.bp (base/cmds/app_process)

cc_binary {
    name: "app_process",

    srcs: ["app_main.cpp"],

    multilib: {
        lib32: {
            suffix: "32",
        },
        lib64: {
            suffix: "64",
        },
    },

여기에 보면 app_process가 선언이 있습니다

사실 android.bp 파일에서 cc_binary 부분이 빌드가 어떻게 빌드가 되서,

왜 /system/bin/ 아래에 있게 되는지 (사실 이게 맞는건지도... ) 잘 모르겠지만 

일단 코드 정황상 이렇게 되어 보입니다

추후 왜 그런지 좀 더 깊이 있게 이해해서 정리해보겠습니다

 

아무튼,  bp파일 문법에 의하면 결국 컴파일되어 있는 app_process라는 파일을 실행하면

srcs에 있는 app_main.cpp (base/cmds/app_process) 파일의 main이 실행 될 것입니다!

 

 

ZygoteInit을 시작하는 프로세스

 

 

app_main.cpp (base/cmds/app_process)

코드 생략된 부분이 많습니다 참고 부탁드리겠습니다.

int main(int argc, char* const argv[])
{
...

    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;
    
    ...
    
    // After the parent dir, we expect one or more the following internal
    // arguments :
    //
    // --zygote : Start in zygote mode
    // --start-system-server : Start the system server.
    // --application : Start in application (stand alone, non zygote) mode.
    // --nice-name : The nice name for this process.
    
    ...
    
    int i;
    for (i = 0; i < argc; i++) {
    ...
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i]));
        
        ...
    }        
        
     // Parse runtime arguments.  Stop at first unrecognized option.
    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.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }
    
 ...
 
     Vector<String8> args;
    if (!className.isEmpty()) {
    // 코드 생략
    } else {
        // We're in zygote mode.
        maybeCreateDalvikCache();
...

    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.");
    }

자 그럼 아까 말씀드린 대로, app_main.cpp의 main 파일부터 확인을 해보면

argc, argv는 이미 아시고 계시겠지만 해당 파일이 시작될 때 뒤에 추가로 적어주던 인자부분 입니다

argc : arguments count (추가로 적어준 인자 부분의 개수, 단 처음 실행 한 코드 부분도 포함된다)

argv : arguments vector (띄어쓰기 구분으로 되어서 인자부분이 분리 되어 저장)

 

아까 그 service 부분을 보면 --zygote,  --start-system-server 가 있으므로

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

여기서 zygote = true, startSystemServer = true 될 것입니다

그리고 -- 부분은 없기 때문에 className은 비어 있게 될 것이기 때문에

if (!className.isEmpty()) {
...
} else {
        // We're in zygote mode.
        maybeCreateDalvikCache();
        ...

else 부분을 타게 될 것입니다

 

고로 우리가 봐야하는 부분은 아랫 부분인 이 부분 일 것입니다 

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.");
}

runtime의 경우 아까 위에서 확인을 했었습니다 (app_main.cpp 파일에서)

 

AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

 

사실 app_main.cpp 파일 내에

 

class AppRuntime : public AndroidRuntime
{
public:
    AppRuntime(char* argBlockStart, const size_t argBlockLength)
        : AndroidRuntime(argBlockStart, argBlockLength)
        , mClass(NULL)
    {
    }

이런 식으로 AndroidRuntime을 상속받은 AppRuntime이 있습니다

(start 메서드의 경우 부모인 AndroidRuntime에 선언 되어 있습니다)

 

 

 

 

 

 

 

 

AndroidRuntime.cpp (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)
{
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());
            
            ...
    static const String8 startSystemServer("start-system-server");
    // Whether this is the primary zygote, meaning the zygote which will fork system server.
    bool primary_zygote = false;
    
    ...
    
     /* start the virtual machine */
    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.
     */
    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);
    
    ...

start에서는

ANDROID_ROOT
ANDROID_ART_ROOT
ANDROID_I18N_ROOT
ANDROID_TZDATA_ROOT

위의 값들에 대한 dir 설정도 하고 vm도 실행을 해줍니다

vm 부분 코드는 꽤 복잡해서 다음에 따로 정리를 해보고 싶습니다 ㅎㅎ

 

결론적으로 우리가 봐야할 부분은 맨 아래 부분인 여기겠네요!

 

    /*
     * 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
        }
    }

즉 결국 className으로 넣어준 의 main을 실행 시키는 상황이 됩니다!

com.android.internal.os.ZygoteInit

이제 ZygoteInit.java  파일의 main (드디어 자바파일을!) 이 실행이 될텐데 이건 타음 포스팅에서 올려보도록 하겠습니다

감사합니다!