본 장에서는 안드로이드의 개념을 소개한
후, 안드로이드의 부팅 순서를 분석해 봄으로써, 안드로이드의
동작 방식을 이해해 보고자 한다. 또한 안드로이드 빌드 시스템을 파악해 보고, 빌드 결과로 얻어진 boot.img를 역 분석하는 과정을 소개하고자
한다.
o 부트로더부터 안드로이드 애플리케이션까지의 구동 순서 소개
- Primary 및 Secondary bootloader
- Application bootloader
- Kernel과 Root File System
- Init process와 init.rc
- 바인더와 서비스 매니저
- Zygote와 Dalvik VM
- 시스템 서비스(mediaserver, system_server,
phone service)
- Activity manager와 android applications
o 안드로이드
빌드 시스템 분석
- 다운로드 & 빌드, 빌드 시스템 분석
- 빌드 결과
분석(Boot.img 해부)
o 본 서의 대상 및 범위, 참조 코드 소개
1. 안드로이드 개요
Google은 안드로이드(Android)를 설계하면서
기존 제품들과는 한 차원 다른 아키텍처를 선 보였다. 먼저 안드로이드의 인터페이스에 해당하는 응응 프로그래밍
언어로써는 이미 전 세계적으로 가장 넓은 사용자 층을 확보하고 있는 Java를 선택했으며, 내부 핵심 운영체제로는 역시 전세계적으로 가장 많은 인기를 누리고 있는 오픈 소스 운영체제인 리눅스(Linux)를 선택하였다. 이 화려한 두 조합이 정상적으로 결합하기
위해서는 생각보다 많은 노력이 필요했는데, 이는 Dalvik
VM(Virtual Machine)의 개발, Java 프레임워크의 구현, Java와 C/C++(Native라고도 함) 간의 인터페이스인 JNI, Native 프레임워크의 구현, C/C++ 기반의 여러 오픈 소스 프로젝트의 최적화 작업 등이 이를 잘 말해주고 있다.
안드로이드의 구조는 그림 1-1에서 볼 수 있듯이 크게 5개의 계층으로 나뉘어져 있는데, 맨 먼저 파란색으로 표시된 최상위 계층은 (1) Java 기반의
응용 프로그램이 차지하고 있으며, 그 아래로는 역시 파란색으로 표시된 (2) Java 기반의 응용 프레임워크(application framework)가
위치하고 있다. 이 응용 프레임워크는 글자 그대로 Java 기반의
응용 프로그램을 구현하기 위해 필요한 내부 루틴으로 이루어져 있는데, 이는 다음 장에서 설명하게 될
시스템 서버(system_server)를 구성하는 내용이기도 하다. 다음으로
노란색으로 표시되어 있는 (3) 안드로이드 런타임은
Dalvik VM과 Core 라이브러리(android.*)로
이루어져 있다. Dalvik VM은 기존의 Java VM과
마찬가지로 Java로 구현된 코드를 번역(컴파일)하는 역할을 담당하게 되며, 안드로이드 Java 라이브러리(jar 파일)로
구성된 Core 라이브러리는 Apache Harmony 프로젝트에
기반하고 있다. 다음으로 녹색으로 표시되어 있는 (4) Native
라이브러리 계층은 C/C++로 구현되어 있으며,
Java 응용 프레임워크에 대비되는 C/C++ 응용 프레임워크(surfaceflinger, mediaserver 등이 주축임)는
물론이고, 각종 오픈 소스를 안드로이드에 맞게 최적화 시킨 라이브러리 들로 구성되어 있다. 마지막으로 빨간색으로 표시되어 있는 최 하단은 (5) 리눅스
커널(Linux kernel)이 차지하고 있으며, 디스플레이(Display), 카메라(Camera), 블루투스(Bluetooth), 공유 메모리(ashmem), 바인더(Binder), USB, 키 패드(GPIO key input,
touchscreen), 와이파이(Wi-Fi), 오디오 사운드, 파워 관리(Power Management) 드라이버 등으로 구성되어
있다.
<안드로이드 소프트웨어
스택의 구조>
1) Java 기반의 응용 프로그램 계층(파란색)
2) Java 기반의 응용 프레임워크(application
framework) 계층(파란색)
3) 안드로이드 런타임(노란색)
- Core Library(파란색) + Dalvik Virtual
Machine(노란색)
4) Native 라이브러리 계층(녹색)
- C/C++로 구성된 각종 라이브러리(각종 오픈 소스를
최적화함)
5) 리눅스 커널(빨간색)
- 본 서의 대상이 되는 부분
<그림>
그림 1-1 Google 안드로이드 아키텍처 [ 출처
– 참고문헌 1 ]
Google이 안드로이드를 설계하면서 고민한 부분을 좀 더 구체적으로 정리해 보면 아래와 같다.
1) Java 기반의 새로운 UI 프레임워크 및 SDK 제공
- Java 개발자를 끌어 들임.
- C++에 비해 보다 쉬운 환경.
- 기존 Oracle(구 Sun) Java의
full 기능과는 차이가 있으며, 새로운 컴포넌트(component)
도입으로 새로 배워야 하는 문제 있음.
2) 새로운 Java VM(Dalvik VM) 개발
- Oracle((구 Sun)과의 Java
라이센스 문제를 타파하기 위해 개발함.
- 기존 방식에 비해
속도 개선함.
3) JNI(Java Native
Interface) : Java <-> C/C++ interface
- 기존 C++ 기반의 UI 대비 프로그래밍의 복잡도 증가함.
- 단순히 JNI의 문제가 아니라, Java 프레임워크와 C/C++ 프레임워크와의 관점임.
4) 새로운 IPC 도입(binder)
- 매우 훌륭한 기법이나, 구현의 복잡도 증가(+ JNI 시 더욱더 복잡해짐).
- 또한 기존 C/C++ applications/libraries가 보유한 IPC(예: socket)와 연계하면서 IPC 기법이 매우 복잡해짐.
- 어쩔 수 없는 측면도
있음 – 이보다 더 잘 만들 수 있을까 ?
5) GNU libc -> Bionic
libc
- 라이센스 문제를 타파하기
위해 작은 libc를 만듦
- BSD license, high-performance, compact size 제공.
- 문제는 C/C++ 기반의 응용 프로그램 및 라이브러리 포팅이 기존 Embedded
Linux 대비 상대적으로 어려워짐.
6) C/C++ 기반의 오픈 소스
최적화
- 스마트 폰 개발에
필요한 대부분의 C/C++ 응용 프로그램 및 라이브러리가 이미 포팅되어 있음.
- 4)에서도 언급했듯이 기존 응용 프로그램 및 라이브러리가 이미 보유하고 있는 IPC를 최대한 유지하면서 최적화 작업 진행함(예: d-bus를 사용하는 Bluetooth).
7) init process의 개선
- init script -> init.rc/init.{hw}.rc로 개선.
- 매우 체계적으로 변모했으나, 새로운 문법이 적용되어 있어 역시 새로 배워야 하는 부담이 있음.
8) Linux kernel의 개선
- Linux kernel의 메인 트리(main tree)와는 다른 별도의
커널 트리(kernel tree)를 가져감으로써 복잡도 증가
- 그러나 최근에 다시
메인 트리에 통합되었음(kernel 3.x 부터)
- 바인더(binder), 애쉬멤(ashmem), 웨이크락(wakelock), 로우메모리킬러(low memory killer), 램콘솔(ram console), 알람(alarm), USB 가젯(usb gadget), ION 메모리 등이 신규로 추가됨.
9) Bootloader 수정
- 새로운 Android boot 파티션 정립.
- Recovery 개념 도입.
- Fastboot 기능 추가함.
10) 새로운 빌드(build) 시스템 도입
- Makefile -> Android.mk 방식으로 바뀜.
- 복잡하지는 않으나, 그래도 새로 배워야 하는 부분이 존재함
<그림>
그림 1-2 단순화한 안드로이드 아키텍처
그림 1-2는 앞서 설명한 복잡한 안드로이드 아키텍처를 사용자 프로세스와
커널의 관점에서 간략화시켜 재구성해 본 것이다. 그림에서 볼 수 있듯이 응용 프로그램 개발자가 Java로 프로그램을 작성하면, C/C++로 구현된 Dalvik VM(라이브러리 형태로 존재함)이 이를 컴파일하게 되며, 여기에 C/C++로 구현된 메인 루틴을 추가하게 되면 기존의 C/C++로 작성된 프로그램과 동일한 형태의 프로세스가 만들어지게 된다. 따라서, Dalvik VM 등 복잡한 부분을 잊어 버린다면, 단순히 Linux Kernel 위에 C/C++로 작성된 여러 가지 응용 프로그램이
동작하고 있는 기존의 구조와 크게 다를 바가 없다고 볼 수 있다.
앞서 설명한 내용을 토대로, Java로 만들어진 안드로이드 응용 프로그램의
구조를 그림으로 그려 보면 다음과 같다.
<그림>
그림
1-3 Java로 만들어진 안드로이드 응용 프로그램의 구조
안드로이드 응용 프로그램
구조 관련하여 좀 더 살펴 보면, Java와 C/C++ 간의
상호 함수 혹은 메서드 호출을 원할하게 해주는 Java Native Interface(줄여서 JNI라고 부름)를 빼 놓고서는 설명이 안될 것이다. 응용 프로그램의 구조가 운이 좋아 그림 1-3과 같이 단순한 형태로
되어 있다면 별 문제가 없겠지만, 대개의 경우는 그 보다는 훨씬 복잡하여 Java 코드에서 C/C++ 라이브러리를 호출한다거나, 반대로 C/C++ 코드 내에서
Java 메서드를 호출해야 하는 상황이 발생할 수 있다. JNI는 이러한 상황을 위해 마련된
인터페이스로 Dalvik VM에서 이를 적절히 처리해 주는 것으로 이해하면 된다. JNI 관련 보다 자세한 사항은 본 서의 범위를 넘어서는 바, 추가
설명은 자제하도록 하겠다.
<그림>
그림 1-4 Java Native Interface
솔직히 이 글을 작성하는
지금은 아주 사소한 내용처럼 느껴지지만, 필자가 처음 안드로이드를 접했을 당시에는 Java로 작성한 응용 프로그램(그림 1-3)이 과연 리눅스 상에서 어떤 식으로 표현(?)될 것인지가 매우
궁금하였던 부분이다. 이 밖에도 안드로이드로 작업을 하다 보면, (다분히
필자의 주관적인 생각이기는 하나) 몇 가지 재미 있는 주제와 만나게 되는데, 이를 정리해 보는 것으로 안드로이드에 대한 간략한 소개를 마칠까 한다.
<왜 개발자들은 안드로이드에
열광하는가 ?>
1) Davik VM과 연동하여 만드는 Java
application
2) Zygote의 개념
3) Binder의 동작 원리
4) init.rc와 거대(?)해진 init process
- 죽은 process를 다시
살리는 부분은 대박 !
5) Fastboot
- 원격 부팅 및 flash image fusing
6) kernel + rootfs로 구성된 boot.img
- 왜 붙여 놓았을까 ? 불편하게 ..
- 부팅 시 boot.img를
통째로 던져 주면, 편할 수 있음. 또한 Security를 염두해 둔 듯 …
7) recovery kernel(recovery.img)
- 두 개의 kernel(boot.img와 recovery.img 내에 각각 하나씩)
8) Android.mk와 새로운 build system
9) Surfaceflinger & 동영상 출력
- graphic 출력은 당연하지만, 동영상
출력도 surfacflinger를 통해서…
10) 여러 가지를 하나로 담아놓은 system_server와 mediaserver
- 설계의 극치
- 다른 이들이 만들었다면, 아마도 10개 이상되는 프로세스가 만들어 졌을 터 …
11) adb
12) debuggered와 tombstone
- Good idea
13) toolbox
- Busybox에 비해 좀 불편
14) 그 밖의 커널 추가 사항: wakelock, ashmem, ram
console, logger 등 …
- ram console이 kernel debugging을
위한 것이지만, 좀 더 적극적인 kernel debugging을
위한 노력이 없는 것은 조금 아쉬운 부분
그 동안 임베디드 리눅스를 기반으로 하는 많은 제품이 있었지만, 안드로이드 처럼 모든 것을 재 해석하고, 잘 다듬어서 하나로 통합한
것은 없었다. 그것이 사람들이 (아니 필자가) 안드로이드에 열광하는 가장 큰 이유일 것이다.
보다 자세한 사항은 아래 site의 내용(pdf 파일 - chapter 1)을 참고하시기 바랍니다.
https://github.com/ChunghanYi/linux_kernel_hacks/blob/master/android_kernel_hacks/AndroidKernelHacks_Chapter1.pdf
댓글 없음:
댓글 쓰기