Android 동작 구조
Android architecture
안드로이드는 스마트 폰을 비롯한 다양한 모바일/임베디드 기기에서 사용할 수 있도록 제작된 Linux 기반의 오픈 소스 소프트웨어 스택입니다. 다음 그림은 해당 구조를 나타냅니다.
Android 플랫폼은 Linux Kernel 기반으로 구성되어 있습니다. 특히, ART(Android runtime)는 스레딩 및 하위 수준의 메모리 관리와 같은 기본 기능에 Linux Kernel을 사용합니다.
Linux Kernel 부분에서 담당하는 부분은 다음과 같습니다.
ü 메모리 등의 자원 관리
ü 보안 설정
ü 전원 관리
ü 하드웨어 장치 드라이버 관리
ü 네트워크 시스템 관리 등
HAL(Hardware Abstraction Layer)하드웨어 추상화는 복잡한 하드웨어 내부를 감추고 일관성 있는 인터페이스를 제공하기 위해, 같은 종류의 하드웨어에 대한 공통 명령어 집합으로 묶는 것을 말합니다.
표준 인터페이스를 제공함으로써, 상위 수준의 Java API Framework에서 기기의 하드웨어 기능을 사용할 수 있습니다. HAL은 여러 라이브러리 모듈로 구성되어 있으며, 카메라 또는 블루투스 모듈과 같은 특정 유형의 하드웨어 구성 요소를 위한 인터페이스를 구현합니다. Java API Framework가 기기의 하드웨어에 접근하기 위한 호출을 발생시키면, Android 시스템이 해당 하드웨어 구성 요소에 대한 라이브러리 모듈을 로드합니다.
ART(Android Runtime)안드로이드 버전 5.0 이상을 실행하는 기기의 경우, 각 App이 자체 프로세스 내에서 ART(Android Runtime) 인스턴스로 실행됩니다. DEX 파일을 실행하여 저용량 메모리 기기에서 여러 가상 머신을 실행하도록 설계되었습니다.
ART의 주요 기능 중 몇 가지를 살펴보면 다음과 같습니다.
ü AOT(Ahead Of Time) 및 JIT(Just In Time) 컴파일
ü 최적화된 garbage collection
ü Android 9 이상에서 앱 패키지의 DEX 형식 파일이 더 최적화되어 간소한 기계 코드로 변환이 가능함
ü 전용 샘플링 프로파일러, 상세 진단 예외 및 크래시 보고, watchpoint를 설정하여 특정 필드를 모니터링할 수 있는 기능을 비롯한 향상된 디버깅 지원 기능
ART에 대한 자세한 사항은 다음 절에서 서술합니다.
Native C/C++ LibrariesART 및 HAL 등의 많은 핵심 Android 시스템 구성 요소와 서비스가 C/C++로 작성된 native 코드를 기반으로 빌드되었기 때문에, 다양한 native 라이브러리들이 Android의 구성 요소에 포함이 됩니다. Android 플랫폼은 이러한 일부 native 라이브러리의 기능을 App에서 사용할 수 있도록 Java API Framework API를 제공합니다. 예를 들어, Android Framework의 Java OpenGL API를 통해 OpenGL ES에 접근하여 2D, 3D 그래픽을 제공할 수 있습니다.
주요 Native C/C++ 라이브러리를 살펴보면 다음과 같습니다.
ü Media Framework
ü OpenGL
ü Webkit
ü Libc
ü SQLite 등
또한 C/C++ 코드를 사용하여 App을 개발하는 경우, Android NDK를 사용하여 이러한 native 라이브러리에 직접 접근할 수 있습니다.
Java API FrameworkAndroid OS의 전체 기능은 Java로 작성된 API를 통해 접근할 수 있습니다. 이러한 Java API는 코어의 재사용, 모듈식 시스템 구성 요소, 서비스를 단순화함으로써 사용자가 Android App을 만들기 위해 필요한 모듈들을 제공해줄 수 있습니다.
Java API Framework는 다음과 같은 요소들을 포함하고 있습니다.
ü View System 리스트, 그리드, 텍스트 상자, 버튼, 웹뷰 등의 다양한 기능을 사용하여 App의 UI를 빌드할 수 있음.
ü Resource Manager 뭐로 빌드했냐에 따라 달라지는 localized strings, 그래픽, 레이아웃 파일과 같이 코드가 아닌 리소스에 대한 접근을 제공함.
ü Notification Manager 모든 App이 상태 표시줄에 사용자 지정 알림을 표시할 수 있도록 지원함.
ü Activity Manager App의 lifecycle을 관리하고 공통 탐색 백 스택 제공
ü Content Provides App이 주소록 앱과 같은 다른 앱의 데이터에 접근하거나 자신의 데이터를 공유할 수 있도록 지원함.
개발자는 Android 시스템 앱이 사용하는 것과 동일한 Framework API에 대한 전체 접근 권한을 갖게 되며, 이를 이용하여 다양한 애플리케이션을 개발할 수 있습니다.
System ApplicationAndroid는 이메일, SMS 메시징, 캘린더, 인터넷 검색, 주소록 등의 주요 App들이 내장되어 함께 제공됩니다. 플랫폼에 기본적으로 포함된 앱은 일반적으로 사용자가 구글 플레이 등의 이용으로 추가적으로 설치하는 앱과 특별히 다른 점이 없기 때문에, 타사의 앱이 사용자의 기본 웹브라우저, SMS 메세지 등이 될 수 있습니다.
시스템 앱은 사용자를 위한 앱으로도 작동하고 개발자가 자신의 앱에서 접근할 수 있는 주요 기능을 제공하기 위한 용도로도 작동합니다. 예를 들어, SMS 메세지 기능을 제공하는 App을 개발하고자 하는 경우, 직접 SMS 기능을 빌드할 필요없이 설치된 SMS 앱을 호출하여 메세지 기능을 이용할 수 있습니다.
Ref: https://developer.android.com/guide/platform?hl=ko
Understanding Android Runtime
Java는 "바이트 코드 하나만으로 다양한 아키텍쳐나 플랫폼에서 작동하게 한다."를 목표로 만들어진 언어입니다. 일종의 VM을 생성하여 사용자의 코드가 동작하게끔 설계함으로써 환경에 관계없이 실행이 가능한데, 안드로이드는 모든 장치에 맞추어 다시 재컴파일할 필요가 없다는 이점을 살리기 위하여, 실행 환경을 Java로 채택하였습니다. 본 행에서는 안드로이드 환경 내에서 프로그램이 어떻게 컴파일 및 실행이 되는지 서술하겠습니다.
History안드로이드 OS 내에서도 일반 OS 내에서 Java가 동작하는 것처럼, 프로그램을 실행시키려고 할 때 JVM(Java Virtual Machine) 프로세스를 켜고 compile된 Java bytecode를 실행시킵니다. 다만 JVM에 대한 라이센스 문제 및 메모리 효율성 문제때문에, 구글은 JVM 대신 거의 비슷하게 동작하는 Dalvik Virtual Machine을 개발하여 안드로이드에 탑재하였습니다. Java의 특성상 VM에서 동작하여 실시간으로 Dalvik code가 기계어로 컴파일이 진행되기 때문에 일반 native code보다 시간이 더 소요된다는 단점이 있습니다. 안드로이드 2.2 버전 이후부터 JIT 컴파일러가 추가되어 좀 더 개선되었지만, 하드웨어에 상당한 부하를 주게 되어, 배터리를 많이 소요했을 뿐만 아니라 RAM을 많이 사용한다는 단점이 생겼습니다. 이에 안드로이드는 5.0 버전 이후부터는 AOT 컴파일러가 기본적으로 적용된 ART VM을 탑재하였고, 안드로이드 7.0 버전 이후의 ART VM에서는 JIT과 AOT 컴파일러를 모두 사용할 수 있도록 하여, 유틸성을 높였습니다.
Compilation Methods프로그램을 만드는 방법은 다양하게 존재합니다. 소스 코드를 native code(기계어)로 변환하여 실행파일을 만드는 compiler 방식(정적 컴파일 방식), 런타임 중에 프로그램을 한 줄씩 읽어들여 실행하는 Interpreter 방식, JIT 방식, AOT 방식 등 다양한 방법이 존재합니다. 본 행에서는 다양한 컴파일 기법 중 JIT (Just In Time) 방식과 AOT (Ahead Of Time) 방식에 대해 서술합니다.
JIT (Just In Time)
정적 컴파일 방식과 Interpreter 방식이 혼합된 컴파일 기법으로, 프로그램을 실제 실행하는 시점에 native code로 변환하는 컴파일 기법입니다. 컴파일 단계에서 소스 코드를 IL(Intermediate Language, 중간 언어)로 형태로 변환합니다. 실행 시점에서 Interpreter 방식으로 변환된 IL 코드를 읽어들이며 기계어 코드를 생성하는데, 이 때 그 코드를 캐싱하여 사용함으로써 같은 코드를 매번 컴파일 하지 않아, 일반 Interpreter 방식보다 속도를 개선할 수 있습니다.
JIT 방식의 장점은 IL 코드가 가상머신 위에서 환경에 맞는 native code (기계어)로 변환되기 때문에, 실행환경에 상관없이 프로그램을 실행시킬 수 있다는 점입니다. 또한, 기존 컴파일러와 다르게 런타임 시에 프로그램에서 사용하는 변수의 값과 같은 dynamic runtime 데이터에 접근할 수 있기 때문에, branch 예측, 빈번한 inlining function의 최적화 등을 진행할 수 있습니다. JIT 방식을 사용하는 언어로 Java와 C# 등이 있습니다.
AOT (Ahead Of Time)
AOT 방식은 JIT 방식과 다르게 컴파일 과정에서 IL 코드를 미리 프로그램 동작 환경에 맞는 native code로 변환시켜놓고, 런타임 시에 미리 변환된 코드를 읽어들이는 방식을 말합니다. 한 번 컴파일이 되면 추후 실행 시 재컴파일할 필요가 없기 때문에 바로 실행이 가능합니다. 또한, 런타임 시에 컴파일을 진행하는 JIT 방식과는 달리, 미리 컴파일을 진행하기 때문에 더 많은 코드 최적화가 가능하여 컴파일 시간은 더 오래걸리지만 실행 시간은 더 빠릅니다. 다만, 특정 환경에 대해서 동작하도록 코드가 컴파일되기 때문에, 다른 환경에서 사용하기 위해서는 원본 코드로 재컴파일 할 필요가 있습니다. 컴파일한 파일의 크기 역시 JIT 방식보다 더 크며, CLR (Common Language Runtime) 위에서 동작하는 C#의 경우 일부 기능은 AOT 컴파일 방식에서는 사용이 불가능합니다.
Android Runtime
위에서 잠깐 언급했다시피, 안드로이드는 Dalvik VM과 ART 두 가지 방식의 런타임을 사용합니다. 다음의 그림은 안드로이드에서 사용하는 Android runtime 과정을 보여주는 그림입니다. 본 행에서는 이 두가지 런타임에 대해 서술합니다.
dex 파일은 Dalvik executable의 약자이며, Dalvik VM에서 실행되는 바이트 코드 파일입니다. Dalvik 방식에서는 해당 dex 파일을 dexopt라는 유틸리티를 이용하여 최적화시킴으로써, Dalvik VM에서 실행 시 성능을 더 높일 수 있는 Odex(Optimized dex) 파일을 생성합니다. 이 후, 런타임 시 생성한 VM 내에서 JIT 컴파일러를 이용하여 Dalvik code를 native code로 변환하며 실행됩니다.
ART (Android runtime)
ART 방식은 적은 메모리, CPU 파워가 낮은 초기 모바일 환경에 최적화되어있는 Dalvik 방식이 하드웨어의 발전에 따른 고성능, 고기능 환경에서 구조적 한계를 보이면서 대체된, AOT를 활용한 런타임 방식입니다. ART는 Dalvik의 바이트 코드 형식인 dex 파일과 완벽하게 호환이 되도록 설계되어, 또 다른 런타임 환경에 대한 개발을 진행할 필요가 없습니다.
Dalvik의 dexopt를 대체하여 dex2oat라는 유틸리티를 사용하는데, dex 파일과 리소스, Native code 파일을 ELF 파일로 만들어서 실행합니다. 파일 타입은 실제로 oat(ELF)이지만, 호환성을 위해서 파일의 확장자는 .dex나 .odex를 사용합니다.
.dex --(dex2oat)--> .oat(ELF) |
ART는 애플리케이션과 시스템 서비스에서 사용되는 런타임으로서 사실 VM이 아닌, 런타임 환경을 제공하는 라이브러리라고 할 수 있습니다. 다만 Dalvik 환경과 논리적으로 동일한 결과를 제공하기 위해 .oat 파일에 가상 머신의 상태를 흉내내기 위한 코드들이 들어가게 됩니다.
앞서 AOT의 장점에서 언급했다시피, AOT에서는 미리 코드를 컴파일하기 때문에, 더 높은 수준의 최적화가 가능하며 native로 동작하기 때문에 훨씬 빠른 속도를 자랑합니다. 변환된 native 코드가 저장공간을 꽤 차지하기는 하지만, 모바일 기기의 저장공간이 점점 커지고 있는 요즘, 큰 단점은 아닌 것으로 보입니다.
안드로이드 7.0부터 ART에 JIT이 추가되었는데, 기존 AOT 컴파일러만을 사용했을 때보다 성능을 개선하였다고 합니다. 따라서, 현재 최신 버전의 ART는 AOT와 JIT, 그리고 profile-guided compilation의 하이브리드 형태로 구성됩니다. profile-guided compilation에 대해서는 다음을 참고하시면 됩니다.
JIT 컴파일러를 추가함으로써, 런타임 성능, 저장 공간, 애플리케이션과 시스템의 업데이트 속도의 향상이 가능합니다. 안드로이드 ART의 JIT 동작구조는 다음과 같습니다.
1. .oat 파일 내에 AOT binary가 존재하여 이용가능한 상태인 경우라면, ART는 직접적으로 해당 native code를 실행시킵니다.
2. 앱이 처음 설치되는 경우나 앱이 실행되는 처음 몇 번(아직 AOT 컴파일이 진행되지 않은 경우)은 AOT 컴파일 방식을 사용하지 않고, 많이 사용되는 메소드의 경우는 JIT를, 잘 사용되지 않는 메소드의 경우는 단순 Interpreter를 사용하여 앱을 실행시킵니다. 이 경우, .oat 파일이 존재하지 않을 뿐만 아니라, 존재하는 경우에도 AOT binary는 포함하고 있지 않습니다.
3. JIT 컴파일러를 사용하는 경우, 해당 애플리케이션만 접근할 수 있는 시스템 디렉토리에 JIT profile data 파일이 생성됩니다.
4. 기기가 한가한 상태거나 충전 중일 경우, AOT 컴파일 데몬(dex2oat)은 생성된 profile 파일을 기반으로 자주 사용되는 코드에 대해 AOT 컴파일을 수행합니다.
5. AOT 컴파일이 정상적으로 수행되었다면, 다음 해당 앱을 실행시켰을 때, 이미 컴파일이 된 메소드의 경우에는 JIT 방식으로 컴파일 되지않고 native code가 바로 실행됩니다. 새로 JIT 방식으로 컴파일된 메소드라면, 경우에 따라 profile에 새로 추가되어 컴파일 데몬이 AOT 컴파일 해줄 수 있습니다.
Ref. https://source.android.com/devices/tech/dalvik/configure https://source.android.com/devices/tech/dalvik/jit-compiler
'좀 열심히 쓴 글' 카테고리의 다른 글
RDP Client fuzzer 개선 보고서 (0) | 2020.06.14 |
---|---|
OpenJPEG Code Coverage 코드 작성 및 알고리즘 설명 (0) | 2020.06.14 |
Unity Mono 분석 (0) | 2020.06.08 |
Frida 및 nodejs 사용할 때 TMI (4) | 2020.06.04 |
Format String field width (0) | 2020.04.09 |