2011년 7월 27일 수요일

Dalvik VM의 개괄적 Source 분석

Dalvik VM은 Android에서 돌아가는 VM이다. 이전에도 포스트 한바와 같이 

Phoneme project, harmony의 코드와도 외형과 핵심 구조체 등이 상당히 유사하다.

이전 포스트 이후 harmony를 대략적으로 분석해 보았으며 

결론은 대략 비슷한데 phoneme와 harmony는 상당히 일치하고 dalvik은 harmony를

바탕으로 만들어진 것이 맞다고 봐야 할듯하다. 

직접적인 코드 외에도 초기화시 로드하는 핵심클래스가 동일한 것들도 몇가지 있다.

예를 들면 다음과 같은 것들이 있다. ( dalvik/vm/Init.c의 dvmJniStartup()을 참고하자. )

org/apache/harmony/luni/platform/PlatformAddress.java
org/apache/harmony/luni/platform/PlatformAddressFactory.java
org/apache/harmony/nio/internal/DirectBuffer.java

간단히 dalvik vm이 실행되는 process를 정리해 보자.

main() -> option을 위한 메모리 할당 -> option 파싱 -> JNI_CreateJavaVM() -> findClass()
-> GetStaticMethodID() -> CallStaticVoidMethod() -> DetachCurrentThread() -> DestroyJavaVM() 

dalvik/vm/main.c의 main() 함수가 entry point이다. 대략 위와 같은 과정을 거치는데 거의 모든 

초기화의 과정이 JNI_CreateJavaVM()에서 이루어진다. 

이후에는 실행시킬 클래스를 findClass()로 실행시킬 main 메소드를 GetStaticMethodID()로 찾는다.

이후 CallStaticVoidMethod()로 class의 main 메소드를 실행시키면 이후의 과정은 main 메소드가

종료할때까지는 intepreter와 dex 파일과의 상호작용이라 봐야 하겠다. 

종료는 DetachCurrentThread()와 DestroyJavaVM()의 두가지 과정으로 볼수 있는데 

Exception에 의한 종료와 정상 종료 크게 두가지 경우가 있을수 있다.

jvm의 경우 일반적으로 따로 Thread를 사용하지 않아도 최소 1개의 쓰레드를 가지고 있는데 

이를 main thread라고 부른다. 일반적으로 static void main(String[] args)으로 선언한 메소드의

코드를 실행하는 역할을 한다고 보면 된다. 


dalvik vm의 전체개요를 이해하기 위해서는 JavaVM, JNIEnv의 두가지 구조체를 이해하는 것이

상당히 중요한데 코드에서는 다음과 같이 정의되어 있다. 

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif

C로 정의된 JNINativeInterface나 JNIInvokeInterface의 경우 디바이스 드라이버를 개발해

보신분들은 상당히 익숙한 구조일듯한 변수와 함수 포인터의 집합으로 구조체를 만들어 놓았다.

정리해 보면

JNIEnv는 대략적으로 dalvik vm의 환경 설정과 관련된 함수들이 쭈욱 모아져 있는데 

코드를 분석하기 위해서는 우선 CallMethod 계열, GetMethod 계열, FindClass 부분을 우선적으로

보는 것이 좋을 것이다.

JavaVM의 경우는 

DestroyJavaVM, AttachCurrentThread, DetachCurrentThread, GetEnv, AttachCurrentThreadAsDeamon

등의 단촐하게 5가지만 있다. 

JNIEnv와 JavaVM 구조체의 함수 포인터들은 각각

JavaVM: main -> JNI_CreateJavaVM()에서 gInvokeInterface의 값으로
JNIEnv: main()->JNI_CreateJavaVM()에서 gNativeInterface의 값으로 

셋팅된다. 


이후의 과정은 서술한 바와 같이 class를 찾고 class의 static main 메소드를 찾고 이를 호출하는

과정이다. 실제 코드는 다음과 같이 되어 있다.

(*env)->CallStaticVoidMethod(env, startClass, startMeth, strArray);


gInvokeInterface에서 찾아보아도 호출하는 함수의 실제 이름도 CallStaticVoidMethod로 동일하다.

CallStaticVoidMethod는 내부에서 CallStaticVoidMethodV를 호출하는데 이함수는 crags, scope

등으로 찾으려하면 잘 나오지 않는데 이는 코드의 선언이 다음처럼 되어 있기 떄문이다. 



 "##"은 변수 또는 함수의 이름을 접합시켜 주는 역할을 한다. 

위의 코드에서는 CallStatic##_jname##MethodA 처럼 사용됐는데 _jname이 컴파일 타임에 결정되어

반드시 결정되어 있어야만 하며 

예를 들어, CALL_STATIC(jbyte,Byte,result.b, false); 의 코드가 있다면

CallStaticByteMethodA와 같은 함수선언이 된것과 같은 효과가 있다. 

그럼 CallStaticVoidMethodV를 잠깐 살펴보자. 

JNI_ENTER()로 쓰레드의 상태를 변경해준후 바로 dvmCallMethodV()를 호출해준다. 

프레임에 공간을 확보한후에 argument를 처리하고 dvmInterpret()를 호출하여 

본격적으로 dex의 opcode의 처리를 시작한다. 

여기서 부터는 다시 복잡해 지므로 자바의 메소드 호출 방식 및 메소프 스택을 

처리하는 법에 관해서는 다음글에서 이어서 포스트한다. 




댓글 9개:

  1. 너무 잘 읽었습니다. 이 글보고 도움 많이 받았어요 ^^
    앞으로도 좋은글 부탁드립니다

    답글삭제
  2. Much of the code under this directory originally came from the Apache
    Harmony project, and as such contains the standard Apache header
    comment. Some of the code was written originally for the Android
    project, and as such contains the standard Android header comment.
    Some files contain code from both projects. In these cases, the header
    comment is a combination of the other two, and the portions of the
    code from Harmony are identified as indicated in the comment.

    안드로이드에 적혀있는 내용입니다.

    답글삭제
  3. 그리고 Apache Harmony는 JAVA의 자유소프트웨어 구현을 만들기 위해 시작하였고 초기에는 GNU와 IBM도 참여했었죠. 나중에는 GPL 코드를 제거하기 위해 새로 구현했었고요. 하모니가 phoneme의 소스를 참고했을 가능성은 매우 낮다고 봐야할 겁니다. 상당수 인터페이스 상의 유사성은 어느 정도는 JNI 때문에 어쩔 수 없는 부분일것입니다. JNI 인터페이스를 가지지 않는 자바 구현을 상상하긴 어려울테니깐요.

    답글삭제
  4. 특히 phoneME가 오픈소스가 되었던 시기를 생각하면 더더욱 있을 수 없는 일라 봅니다. 2006년 말이 되어서야 phoneME가 공개되었습니다. 하모니는 2005년부터 이미 진행중이던 프로젝트였죠.

    답글삭제
  5. 흠 그리고 "##"은 C의 문법은 아니고 GCC의 문법 이부분 잘못 설명하신 것 같습니다. preprocessor의 concatenation은 ISO C 표준입니다.

    답글삭제
  6. 지적해주셔서 감사합니다. 해당 구절은 수정하였습니다.

    답글삭제
  7. 달니나옴님의 Apache Hanmony와 관련된 의견도 감사합니다.
    글에서도 적었지만 phoneme와 harmony의 유사성을 얘기하는글은 구글 검색으로도 찾아보기 어렵기는 합니다.

    제가 글에서 유사성을 얘기하는 근거는 main() 엔트리에서 초기화 해주는 부분부터 클래스를 로딩하고 인터프리터를 통해 실행시키는 전체적인 형태가 유사하기 때문이며 native method를 호출하는 인터페이스인 JNI 떄문은 아닙니다.

    유사한 부분이 인터페이스에 해당하는 부분이다는 말에는 동의하며
    JVM의 핵심을 부분을 core library, intepreter와 bytecode의 실행, GC, frame stack등으로 본다고 했을때 아마도 님의견대로 상당히 다를꺼라 생각합니다.

    답글삭제
  8. 좋은글 감사합니다.
    안드로이드 소스를 분석하고자 하는 대학생입니다.
    많은 부분 궁금한 점이 있어서 여쭤볼것들이 있는데 이메일 좀 가르쳐 주십시오.
    jeonjoo918@gmail.com 저의 메일이니 이쪽으로 메일주소 가르쳐 주시면 감사합니다.

    답글삭제