96

인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

  • View
    486

  • Download
    50

Embed Size (px)

DESCRIPTION

송형주, 김태연, 박지훈, 이백, 임기영 지음 | 임베디드 & 모바일 시리즈 _ 006 | ISBN: 9788992939584 | 30,000원 | 2010년 09월 3일 발행 | 512쪽

Citation preview

Page 1: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석
Page 2: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석
Page 3: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

인사이드

안드로이드

Page 4: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

iv

•목 차•

1장 안드로이드 프레임워크 개요 11.1 안드로이드의 소스 코드 구조 ...............................................................3

1.2 부팅 프로세스로 알아보는 안드로이드 프레임워크 ..........................3

2장 안드로이드 개발 환경 구축 72.1 호스트 환경 구성 ...................................................................................7

2.1.1 VirtualBox 설치 ........................................................................8

2.1.2 우분투 설치 ...............................................................................9

2.2 안드로이드 플랫폼 빌드 환경 구축 ................................................... 10

2.2.1 빌드 유틸리티 .........................................................................10

2.2.2 Repo 설치 ................................................................................12

2.2.3 안드로이드 플랫폼의 소스 코드 내려받기 ...........................12

2.2.4 안드로이드 플랫폼 빌드 ........................................................13

2.3 안드로이드 SDK 개발 환경 구축 ...................................................... 13

2.3.1 이클립스 개발 환경 구축 .......................................................14

2.3.2 안드로이드 SDK starter 패키지 다운로드 ...........................14

2.3.3 이클립스용 ADT 플러그인 설치 ...........................................15

2.3.4 안드로이드 SDK 경로 설정 ...................................................17

2.3.5 안드로이드 SDK 설치 ............................................................18

2.4 안드로이드 애플리케이션 개발 ......................................................... 20

2.4.1 Hello 애플리케이션 작성 .......................................................20

Page 5: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

v

2.5 애플리케이션 프레임워크 소스 레벨 디버깅 ................................... 23

2.5.1 애플리케이션 프레임워크 소스 로딩 ....................................23

2.5.2 HelloWorld 프레임워크 소스 레벨 디버깅 ..........................27

2.6 정리 ..........................................................................................31

3장 init 프로세스 333.1 init 프로세스의 실행 과정 ................................................................ 33

3.2 init 프로세스의 소스 코드 분석 ........................................................ 35

3.3 init.rc 파일 분석 및 실행 ................................................................... 46

3.3.1 액션 리스트 ............................................................................47

3.3.2 서비스 리스트 .........................................................................50

3.3.3 init.rc 파싱 코드 분석 .............................................................51

3.3.4 액션 리스트 및 서비스 리스트의 실행 ..................................55

3.4 디바이스 노드 파일 생성 ................................................................... 59

3.4.1 정적 디바이스 노드 생성 .......................................................60

3.4.2 동적 디바이스 감지 ................................................................66

3.5 프로세스 종료와 재시작 ..................................................................... 67

3.5.1 프로세스 재시작 코드 분석....................................................68

3.6 프로퍼티 서비스 ................................................................................. 72

3.6.1 프로퍼티 초기화 .....................................................................73

3.6.2 프로퍼티 변경 요청 처리 .......................................................75

3.7 정리 ..................................................................................................... 78

Page 6: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

vi

4장 JNI와 NDK 794.1 안드로이드와 JNI ............................................................................... 79

4.1.1 왜 안드로이드에서 JNI를 알아야 하는가? ...........................79

4.2 JNI의 기본 원리 이해 ......................................................................... 82

4.2.1 자바에서 C 라이브러리 함수 호출하기 ................................82

4.2.2 정리 ..........................................................................................95

4.3 JNI 함수 이용하기 .............................................................................. 96

4.3.1 JNI 함수를 활용하는 예제 프로그램의 구조 ........................96

4.3.2 자바측 코드 살펴보기 (JniFuncMain.java) .........................97

4.3.3 JNI 네이티브 함수의 코드 살펴보기 ....................................99

4.3.4 컴파일 및 실행 결과 .............................................................115

4.3.5 안드로이드에서의 활용 예 ..................................................116

4.4 C 프로그램에서 자바 클래스 실행하기 .......................................... 116

4.4.1 호출 API 사용 예제 ..............................................................117

4.4.2 컴파일 및 실행 ......................................................................122

4.4.3 안드로이드에서의 활용 예: Zygote 프로세스 ....................124

4.5 JNI 네이티브 함수 직접 등록하기 .................................................. 125

4.5.1 네이티브 라이브러리 로드 시에 JNI 네이티브 함수

등록하기 ...............................................................................126

4.5.2 안드로이드에서의 활용 예...................................................131

4.6 안드로이드 NDK로 개발하기 ......................................................... 138

4.6.1 안드로이드 NDK 환경 설정 ................................................139

4.6.2 안드로이드 NDK 개발 따라하기 ........................................143

4.6.3 정리 ........................................................................................154

Page 7: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

vii

5장 Zygote 1555.1 Zygote란 무엇인가? .......................................................................... 155

5.1.1 Zygote를 통한 프로세스의 생성 ..........................................156

5.2 app_process로부터 ZygoteInit class 실행 ..................................... 161

5.2.1 AppRuntime 객체 생성 .......................................................162

5.2.2 AppRuntime 객체 실행 .......................................................163

5.2.3 달빅 가상 머신의 생성 .........................................................164

5.2.4 ZygoteInit 클래스의 실행 ....................................................166

5.3 ZygoteInit 클래스의 기능 ................................................................ 167

5.3.1 /dev/socket/zygote 소켓 바인딩 ........................................169

5.3.2 애플리케이션 프레임워크에 속한 클래스와

플랫폼 자원의 로딩 .............................................................171

5.3.3 SystemServer 실행 ...............................................................175

5.3.4 새로운 안드로이드 애플리케이션 실행 ..............................179

6장 안드로이드 서비스 개요 1836.1 예제 프로그램 : 안드로이드 서비스 동작 이해 .............................. 183

6.2 안드로이드 서비스 분류 ................................................................... 187

6.3 안드로이드 애플리케이션 서비스 ................................................... 188

6.3.1 애플리케이션 서비스의 분류 ...............................................190

6.4 안드로이드 시스템 서비스 ............................................................... 204

6.4.1 시스템 서비스의 분류 ..........................................................205

6.5 시스템 서비스의 실행 ..................................................................... 208

6.5.1 미디어 서버의 실행 코드 분석 .............................................209

6.5.2 시스템 서버의 실행 코드 분석 .............................................212

6.6 안드로이드 서비스 프레임워크와 바인더 드라이버 개요 및

용어 정리 ........................................................................................... 216

Page 8: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

viii

7장 안드로이드 바인더 IPC 2197.1 리눅스 메모리 공간과 바인더 드라이버 ......................................... 220

7.2 안드로이드 바인더 모델 ................................................................... 222

7.2.1 바인더 IPC 데이터의 전달...................................................224

7.2.2 바인더 IPC 데이터의 흐름...................................................226

7.2.3 바인더 프로토콜 ...................................................................227

7.2.4 RPC 코드와 RPC 데이터 .....................................................229

7.2.5 바인더 어드레싱 ...................................................................230

7.3 안드로이드 바인더 드라이버 분석 .................................................. 233

7.3.1 프로세스 관점에서의 서비스 사용 ......................................233

7.3.2 바인더 드라이버 관점에서의 서비스 사용 .........................238

7.3.3 바인더 드라이버 함수 분석..................................................243

7.4 컨텍스트 매니저 ............................................................................... 281

7.4.1 컨텍스트 매니저의 동작 ......................................................282

7.5 정리 .................................................................................................... 286

8장 안드로이드 서비스 프레임워크 2878.1 서비스 프레임워크 ........................................................................... 287

8.2 서비스 프레임워크의 구조 ............................................................... 290

8.2.1 계층별 구성 요소의 배치 .....................................................290

8.2.2 계층별 구성 요소의 상호작용 ..............................................292

8.2.3 클래스 구조 ...........................................................................296

8.3 동작 메커니즘 ................................................................................... 298

8.3.1 서비스 인터페이스 ...............................................................299

8.3.2 서비스 ....................................................................................306

8.3.3 서비스 프록시 .......................................................................310

8.3.4 바인더 IPC 처리 ...................................................................314

Page 9: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

ix

8.4 네이티브 서비스 매니저 ................................................................... 316

8.4.1 서비스 매니저 개요 ..............................................................316

8.4.2 서비스 매니저 클래스 ..........................................................319

8.4.3 서비스 매니저의 동작 ..........................................................321

8.5 네이티브 서비스 제작하기 ............................................................... 351

8.5.1 HelloWorld 시스템 서비스 설계 .........................................352

8.5.2 HelloWorld 서비스 인터페이스 ..........................................353

8.5.3 HelloWorld 서비스 ..............................................................354

8.5.4 HelloWorld 서비스 프록시 ..................................................357

8.5.5 HelloWorld 서비스 실행 ......................................................358

8.6 정리 .................................................................................................... 363

9장 네이티브 시스템 서비스 분석 3659.1 카메라 서비스 ................................................................................... 365

9.2 카메라 애플리케이션........................................................................ 366

9.3 카메라 서비스 프레임워크 ............................................................... 370

9.3.1 카메라 서비스 프레임워크의 계층 구조 ............................370

9.3.2 카메라 서비스 프레임워크의 클래스 ..................................371

9.4 카메라 서비스 프레임워크의 동작 .................................................. 373

9.4.1 카메라 서비스 초기화 ..........................................................373

9.4.2 카메라 서비스 연결 과정 .....................................................374

9.4.3 카메라 서비스 연결 과정 분석 .............................................376

9.4.4 카메라 설정 및 제어 과정 ....................................................380

9.4.5 카메라 설정 및 제어 과정 분석 ............................................381

9.4.6 카메라 이벤트의 처리 과정..................................................381

9.4.7 카메라 이벤트 처리 과정 분석 .............................................383

9.5 정리 .................................................................................................... 384

Page 10: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

x

10장 자바 서비스 프레임워크 38710.1 자바 서비스 프레임워크 ................................................................... 387

10.1.1 자바 서비스 프레임워크의 계층별 요소 .................................389

10.1.2 자바 서비스 프레임워크의 클래스별 상호작용 .................392

10.2 동작 메커니즘 ................................................................................... 397

10.2.1 자바 서비스 프레임워크 초기화 ..........................................397

10.2.2 Binder ....................................................................................398

10.2.3 BinderProxy ..........................................................................404

10.2.4 Parcel .....................................................................................407

10.3 자바 시스템 서비스 구현 .................................................................. 411

10.3.1 알람 매니저 서비스의 구조 분석 .........................................412

10.3.2 HelloWorldService 시스템 서비스의 구현 ........................415

10.3.3 HelloWorldService 시스템 서비스의 이용 ........................419

10.3.4 HelloWorldService 시스템 서비스 빌드 ............................422

10.4 자바 서비스 매니저 .......................................................................... 424

10.4.1 자바 서비스 매니저 소개 .....................................................424

10.4.2 BinderInternal ......................................................................426

10.4.3 자바 서비스 매니저의 동작..................................................428

10.5 AIDL을 이용한 서비스 프록시와 스텁의 구현 .............................. 435

10.5.1 aidl 파일에 서비스 인터페이스 정의하기 ..........................436

10.5.2 AIDL 컴파일러를 통한 서비스 인터페이스, 스텁,

프록시 클래스 생성 .............................................................437

10.5.3 인터페이스를 구현한 서비스 작성 ......................................439

10.5.4 서비스 사용자가 서비스 인터페이스를

사용할 수 있게 만들기 .........................................................440

10.6 정리 .................................................................................................... 440

Page 11: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

xi

11장 자바 시스템 서비스 동작 분석 44311.1 액티비티 매니저 서비스 ................................................................... 443

11.2 액티비티 매니저 서비스를 통한 서비스 생성 코드 분석 .............. 447

11.2.1 Controller 액티비티 - startService() 메서드 호출 ............447

11.2.2 액티비티 매니저 서비스의 startService() 메서드 호출 과정

(바인더 RPC 활용) ...............................................................448

11.2.3 액티비티 매니저 서비스 - startService()

스텁 메서드 실행 ..................................................................455

11.2.4 Activity�read 클래스의 main() 메서드 실행 ..................459

11.2.5 액티비티 매니저 서비스 - attachApplication()

스텁 메서드 처리 ..................................................................465

11.3 정리 .................................................................................................... 473

부록 AIDL 언어의 문법 476

찾아보기..............................................................................478

Page 12: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

xii

송형주

필자가 안드로이드를 처음 접한 2009년 5월. 대성공을 거둔 애플의 아이폰에 유일한 대항마로 여겨

지는 안드로이드에 대한 개발자들의 관심이 연일 커지던 시기였다. 출판사들은 앞다투어 안드로이드

SDK를 이용한 애플리케이션 개발 서적을 출판하기 시작했고, 필자도 이러한 시류에 편승해 안드로이

드 애플리케이션 프로그래밍을 처음 시작했다.

하지만 당시 시스템 프로그래밍 분야에 종사했던 필자의 직업 때문이었는지 점점 안드로이드 애플

리케이션 작성보다는 안드로이드 내부 구조에 관심이 더 가게 됐다. 그래서 오픈소스로 제공되는 안

드로이드를 한번 분석해보기로 마음을 먹었지만 이내 커다란 벽에 부딪히고 말았다. 안드로이드 내부

구조를 분석한 서적 및 인터넷 자료가 거의 없었기 때문이다. 왠지 계란으로 거대한 바위를 치고 있는

듯한 느낌이었다.

그래서 접근 방법을 달리해 ‘수원 안드로이드 플랫폼’ 스터디 모임을 결성하고 필자처럼 안드로이드

플랫폼 구조를 분석하는 데 관심이 많은 여러 멤버들과 함께 안드로이드라는 거대한 정글을 탐험해

보기로 마음먹었다. 결과는 대성공이었다. 필자를 포함한 활동 분야가 다양한 개발자들은 매주 토요

일마다 휴일도 반납한 채, 서로 모여서 치열하게 고민했고 각자의 의견을 교환하며 안드로이드 분석과

관련한 자료를 정리해 갔다.

이 책은 이러한 스터디 모임에서 정리한 자료, 수도 없이 겪었던 시행착오, 그리고 치열한 고민의 산

물이다. 비록 이 책에서 안드로이드 프레임워크의 모든 부분을 설명하지 못했지만, “눈길을 걸을 때 함

부로 걷지 마라. 내 발자국이 뒤에 오는 자의 길잡이가 될 지어니”라는 시의 문구처럼 안드로이드 플

랫폼에 관심이 있고, 처음 접하려는 사람들에게 길잡이가 될 수 있도록 노력했다.

비록 집필까지 함께 하지는 못했지만 매주 토요일 함께 안드로이드 플랫폼을 연구했던 스터디 멤버

들에게 감사드린다. 마지막으로 집필 시기가 임신/육아 시기와 맞물려 힘들어하면서도 항상 못난 남

편을 응원해주던 나의 사랑하는 아내 현정이와 집필 때문에 많이 못 놀아준 아들 승민이에게 진심으

로 고마움을 전한다.

•저 자 서 문•

Page 13: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

xiii

김태연

안드로이드 플랫폼이 처음 소개된 후 현재까지도 빠른 속도로 업그레이드되고 있습니다. 버전이 업

그레이드되면서 새롭게 추가/변경되는 기능으로 인해 개발 현장에서 이 변화의 속도를 따라잡기란

쉽지 않은 것 같습니다. 특히 안드로이드를 구성하는 기본적인 기술들이 다양하기 때문에 전체를 폭

넓게 이해하고 대응하기란 더욱 힘들 것으로 생각합니다. 이런 상황에서 안드로이드 시스템 프로그

래머는 리눅스, C++, Java 등에 관한 기본 지식뿐만 아니라, 안드로이드 플랫폼 역시 잘 이해하고 있

어야 합니다.

안드로이드 플랫폼을 배우는 좋은 방법은 무엇일까요? 저는 고등학교 수학을 배울 때, 먼저 “수학

의 정석”에 나온 기본 원리들을 익힌 후 응용 문제를 풀었던 것처럼, 안드로이드 플랫폼을 구성하고

있는 핵심 원리를 먼저 익히는 것이라고 생각합니다. 이 책은 안드로이드 플랫폼의 필수 요소인 init

프로세스, 바인더, JNI, Zygote, 서비스 프레임워크, 핵심적인 시스템 서비스 등의 동작원리에 관해 설

명하고 있습니다.

핵심 원리는 그 성격상 쉽게 변하는 것이 아닙니다. 집필을 시작할 때, 안드로이드는 1.6(도넛) 버전

이었지만 원고를 마감할 시점에는 2.2(프로요) 버전이 공개되었습니다. 그러나 책의 내용이 기본 원리

에 관해 설명하고 있기 때문에 두 번의 버전 업그레이드에도 불구하고 원고의 수정사항은 별로 없었

습니다. 이점을 생각하면 개발자들이 이 책의 내용을 우선 습득하는 것은 안드로이드 플랫폼을 배우

는 좋은 출발점이 될 것이라 확신합니다.

이 책의 내용은 안드로이드 플랫폼에 관한 정보가 전무하였을 시기에 가장 원초적인 방법이자 확

실한 방법인 소스코드 분석을 통하여 얻은 것입니다. 이 분석 작업은 6개월 동안 개인 시간 및 주말을

반납하면서 안드로이드 플랫폼 스터디에 참여했던 많은 개발자 분들이 있었기에 가능했습니다. 스터

디에 참여하셨던 모든 분들에게 감사합니다.

특히 스터디를 조직하여 끝까지 이끌어주시고 책 집필 과정에 참여할 수 있도록 해주신 송형주 형

님께 감사 드립니다. 그리고 스터디 시간보다 더 긴 시간을 원고 집필하느라 고생하신 이백 큰형님, 임

기영 형님, 박지훈씨께도 감사 드립니다. 고생 많으셨습니다. 또한, 원고 완성을 위해 물심양면으로 지

Page 14: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

xiv

원해주신 위키북스 박찬규 사장님, 이대엽님에게도 감사 드립니다. 사장님, 결국 저희가 해냈습니다!

개인적인 감사의 글을 끝으로 저자 서문을 마무리하고자 합니다. 먼저 4년간 저를 지도해 주고 계시

는 채흥석 교수님께 깊이 감사 드립니다. 안드로이드 스터디를 처음 소개해준 연찬, 옵티머스 Q 개발

팀, 특히, 매일 아침 빵과 우유를 사주시며 격려해 주신 조상현 선임님, 설미영 주임님, 원고를 꼼꼼히

리뷰해주신 이동훈 주임님께 감사 드립니다. 원고 쓰는 동안 많은 응원과 격려를 아끼지 않은 301기

동기들에게도 감사합니다. 긴 원고 내용을 베타리뷰 해준 연구실 동생들, 선열, 정호, 현재, 연지에게

도 감사합니다. 늘 사랑해 주시고 보살펴 주시고 걱정해 주시는 부모님, 동생가족에게도 감사합니다.

신혼여행 중에도 원고를 작성하던 간 큰 남편을 넓은 마음으로 이해해주는 사랑하는 아내 진숙에게

도 감사합니다. 부인, 앞으로 태어날 우리 공주님하고 행복하게 살아요! 사랑합니다.

박지훈

수원 안드로이드 플랫폼 스터디를 통해 안드로이드라는 새로운 모바일 플랫폼을 알아가는 것은 나에

게 새로운 도전이었다. SoC(System On Chip) 검증이라는 소프트웨어보다는 하드웨어 개발에 가까

운 일을 하는 나에게 안드로이드가 가진 수많은 기술 도메인들은 나의 지적 호기심을 충족시켜줄 것

같았다. 그리고 실제로 그랬다. 안드로이드 커널을 분석하면서 리눅스의 깊은 곳을 들여다 볼 수 있었

고, 프레임워크 전반에 걸친 자바의 유연함과 각종 핵심 라이브러리들을 이어주는 JNI, 그리고 프로

세스들의 보모와 같은 Zygote 등 안드로이드의 구성요소를 알아가는 것은 흥미로운 일이었다. 그리

고 코드레벨에서 그 모든 것을 들춰 볼 수 있다는 것이 오픈소스인 안드로이드가 가진 가장 큰 매력이

었다.

이 책은 오픈소스인 안드로이드의 코드를 하나하나 따라가면서 분석한 책이다. 불 꺼진 대학강의실

에서 빔 프로젝트가 뿌려대는 소스코드를 통해 몇몇 지적 개척자들이 아직 미지의 영역인 안드로이

드 프레임워크를 탐험하면서 발견한 내용을 기록한 일지이기도 하다. 구글이 꼭꼭 숨겨놓은 코드의

비밀을 이해할 때면 너도나도 짜릿한 기쁨을 느끼곤 했다. 이젠 이 기쁨의 과정들을 이 책을 통해 독

자들에게 물려주고 싶다.

스터디의 결과물을 좀더 잘 갖추어진 형태로 남기고자 시작한 일이지만, 늦은 시간 퇴근 후에 정신

을 가다듬고 집필을 한다는 게 쉽지는 않았다. 그리고 같이 집필하시는 분과의 의견 조율 또한 많이

힘들었지만, 이것이 지금의 책이 나올 수 있었던 원동력이었던 것 같다. 밤늦은 시간까지 독자의 입장

에서 원고 검토를 도와준 임명택씨와 바쁜 와중에도 전문가적 입장에서 꼼꼼히 바인더 IPC 원고 리

뷰를 해준 이솝의 고현철님께 감사의 말씀을 드리고 싶다.

Page 15: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

xv

이백

오프라인 스터디 모임에 참여하여 안드로이드 플랫폼을 접한 것이 어느덧 일 년이 훌쩍 넘었다. 그 동

안 유행처럼 모였다 사라지는 많은 모임 중에 하나로 끝날 수 있었지만, 지금까지 유지되고, 결과물을

낼 수 있었던 것은 스터디 모임의 각 구성원들의 적극적인 참여가 있었기 때문이 아닐까 생각한다. 되

돌아 보면 구성원 간에 협동하고 논쟁하며 좋은 일, 싫은 일 많이 있었지만, 내겐 이 시간들이 잊을 수

없는 추억이 될 것이다. 그래서 스터디 구성원 모두에게 감사 드리며, 출판을 허락해 주신 위키북스 관

계자 분들에게도 감사의 맘을 전하고 싶다.

마지막으로 그 동안 스터디 참여와 집필을 위해서 가족들과 같이 지내는 시간이 자연히 줄어 들었

지만, 싫은 소리 한번 하지 않는 아내 박미라와 얼굴도 잘 못 보는 아빠를 많이 좋아해 주는 아들 준민

에게 사랑한다고 전하고 싶다.

임기영

구글의 안드로이드 OS가 발표되었을 때, 이유 없이 가슴이 두근거렸습니다. 20여 년 전 Apple,

Amiga, IBM, MSX 등이 퍼스널 컴퓨터 시장에서 치열하게 경쟁하였고, 뒤늦게 뛰어든 IBM이 개방형

구조의 컴퓨터를 발표하면서, IBM 호환 퍼스널 컴퓨터들이 순식간에 시장을 잠식해 버리고 맙니다.

임베디드 기기의 시장 역시 그 무렵과 유사한 행보를 보이고 있습니다. 휴대폰 제조사들은 지극히

폐쇄적인 운영체제와 하드웨어로 전화라는 고유의 기능을 수행하는 기기를 생산해 냄으로써, 각자의

영역을 확대해 가며, 시장을 독식해 왔습니다. 그러나 기술의 발전은 손안에 있는 조그마한 기기가 점

차로 똑똑해질 수 있는 기반을 만들어왔고, 여기에 매우 영리한 Apple이 참여함으로써 순식간에 시

장을 잠식해 나가고 있습니다.

하지만 아직까지 많은 부분에서 폐쇄적인 이 시장에 드디어 ‘개방성’을 선언한 구글이 안드로이드

플랫폼을 가지고 시장에 뛰어들었습니다. 예상대로 개방성을 무기로 하는 안드로이드 플랫폼은 시장

의 많은 제조사들을 끌어 들였고, 점차 시장의 큰 부분을 차지해 가고 있습니다. 앞으로 멀지 않은 시

간 안에, 구글이 이끌어나가는 이 오픈소스 프로젝트가 IBM 호환 PC가 그러했듯, 우리의 삶에 얼마

나 큰 영향을 주게 될지 지켜보는 것은 매우 흥미로운 일일 것이라고 생각합니다.

그러나 구글의 이 흥미로운 플랫폼에 대한 자료는 아직까지도 많지 않은 상태입니다. 어플리케이션

개발자가 절대적으로 많을 수 밖에 없는 특성 탓에, 어플리케이션 개발 관련된 서적은 많이 있습니다

만, 프레임워크 내부의 동작이나, 플랫폼의 핵심요소들에 대해 분석할 때 참고가 될 만한 자료들은 여

전히 부족한 상황입니다.

Page 16: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

xvi

2008년 무척이나 무더웠던 여름, 이 흥미로운 플랫폼에 관심을 갖고 모인 많은 분들과 스터디를 시

작할 즈음에는 이렇게 책을 쓰게 되리라고는 생각지도 못했습니다. 단지, 플랫폼에 대해 이해하고 싶

지만 너무 방대한 플랫폼에 다가가기 힘들어 답답해 했었지요. 그러나 스터디를 통해 여러 멤버들과

지식을 교환하고 부족한 지식들을 채워나감으로써, 안드로이드라는 커다란 플랫폼에 대해 한발 한발

나아가며 이해할 수 있었습니다. 이제 스터디를 통해 얻은 지식을 이 책을 빌어 공유하고자 합니다. 보

잘것없는 지식이지만, 이 책이 안드로이드 플랫폼이라는 거대한 산에 발을 내딛는 독자들의 답답함이

해소되는데 조금이나마 도움이 되었으면 하는 바램입니다.

그간 많은 도움을 주셨던 집필 리더 송형주님, 성실함으로 많은 것을 느끼게 해 주셨던 박지훈님, 늘

명쾌함과 자신감, 유쾌함으로 지식을 나누었던 김태연님, 오랜 경험에서 느낄 수 있는 연륜으로 이끌

어주신 이백님, 소중한 기회를 주신 위키북스 여러분께 이 자리를 빌어 다시 한번 깊은 감사를 드리며,

매번 원고가 늦어져 힘들게 해드려 진심으로 미안하다는 말씀을 전합니다.

마지막까지 회사일이다, 집필이다 하면서 집안에 소홀해졌음에도 한결 같은 모습으로 지지해준 사

랑하는 아내 혜주에게 진심으로 사랑을 전합니다.

Page 17: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

xvii

모바일 환경에서의 개발자에게 필요한 능력은 무엇일까?

다양한 대답이 가능하겠지만, 타겟 장치에 적용된 프레임워크에 대한 이해도 필수적이라고 생각된

다. 이는 다시 말하면 윈도우나 리눅스 등 운영체제의 동작 원리를 제대로 이해하는 프로그래머나 웹

브라우저의 동작 원리를 이미 숙지하고 있는 웹프로그래머가 고급 개발자로 대우 받는 것과 비슷한

얘기일 것이다.

마찬가지로 모바일 환경에서의 프레임워크의 동작 원리를 이해하는 것은 고급 개발자로 나아가기

위한 필수 관문이다. 이것은 안드로이드 개발자에게도 그대로 적용된다. 하지만 안드로이드 프레임워

크의 동작 원리와 관련된 자료들은 인터넷이나 시중의 서점을 통해서 구하기가 굉장히 어렵다. 구글

또한 구글 IO(구글에서 매년 개최하는 개발자 행사)의 몇몇 세션에서 발표된 자료를 제외하고는 아직

까지 안드로이드 프레임워크에 대한 설명을 속 시원히 해주지 않고 있는 실정이다.

현재로서는 안드로이드 프레임워크의 동작 원리를 제대로 이해하는 방법은 가장 원초적인 안드로

이드 소스코드를 직접 살펴보는 방법밖에 없다. 저자들도 안드로이드 프레임워크 분석을 위한 자료

가 너무 부족한 터라 결국 안드로이드 소스코드 분석을 통해 필요한 내용을 얻을 수밖에 없었다. 그러

나 방대한 안드로이드 소스코드 중에서 어디서부터 분석을 시작해야 할지 도무지 감이 오지 않았다.

다행히 구글 IO 2008 행사의 안드로이드 세션 중 ‘Anatomy & Physiology of an Android’ (http://

sites.google.com/site/io/anatomy--physiology-of-an-android)라는 제목의 프레젠테이션 자료에서

소개된 안드로이드 프레임워크 초기화 과정 등을 포함한 여러 내용들에서 안드로이드 프레임워크 분

석의 방향을 결정하는 데 많은 도움을 얻을 수 있었다.

이 책을 처음 작성할 시기에는 안드로이드 1.5 버전(코드명 Cupcake)을 중심으로 분석을 시작했으

나 집필이 마무리될 쯤에는 안드로이드 2.2 버전(코드명 Froyo)이 발표됐다. 물론 책의 내용은 현재

최신 버전인 안드로이드 2.2를 기반으로 작성했지만, 프레임워크 특성상 1.5버전부터 2.2버전까지 프

레임워크 관련해서 소스코드의 커다란 변화가 없어서 버전에 크게 구애받을 필요는 없다. 2010년 하

반기에 출시 예정인 3.0(코드명 Gingerbread) 버전에서도 프레임워크에 대한 내용 변경은 알려진 게

서 문

Page 18: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

xviii

없으나, 이 책의 내용은 계속 유효할 것이라 생각한다.

마지막으로 이 책에서 소개하는 안드로이드 프레임워크에 대한 내용은 방대한 안드로이드 내부에

대한 전체적인 설명이 아니라, 프레임워크 내부의 동작 원리를 이해하는 가이드라고 볼 수 있다. 책 내

용을 바탕으로 현업에 필요한 안드로이드 소스코드 부분에 대한 분석을 시작한다면 좀 더 쉽게 원하

는 바를 이룰 수 있을 것이라 생각된다.

마지막으로 이 책의 내용과 관련한 질문은 www.kapg.org에 문의하기 바란다.

Page 19: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

1

안드로이드는 모바일 플랫폼이다. 구글이 제공하는 안드로이드 개발자 가이드 사이트 http://

developer.android.com에서는 안드로이드를 운영체제와 미들웨어, 그리고 핵심 애플리케이션을 포

함한 모바일 디바이스 개발에 적합한 소프트웨어 스택이라 정의한다. 즉, 안드로이드는 모바일 디바

이스용 애플리케이션을 쉽게 제작하기 위한 소프트웨어 프레임워크를 제공해 준다.

실제로 안드로이드 애플리케이션을 개발해 본 경험이 있는 독자라면 안드로이드 프레임워크를 속

속들이 알지 않아도 구글에서 제공하는 안드로이드 SDK를 통해 안드로이드 상에서 동작하는 애플

리케이션을 손쉽게 개발할 수 있다는 사실을 잘 알고 있을 것이다. 이것은 안드로이드가 이미 잘 정의

된 소프트웨어 프레임워크를 개발자에게 제공하여 개발자가 별도의 지식을 갖추지 않고도 빠른 시간

내에 안드로이드 애플리케이션을 개발할 수 있게 해주기 때문이다.

APPLICATIONS

APPLICATION FRAMEWORK

LIBRARIES

LINUX KERNEL

Home Dialer SMS/MMS IM Browser Camera Alarm Calculator

Contacts Voice Dial Email Calender Media Player Albums Clock ...

Noti�cation Manager

Window manager

Activity Manager Content Providers View System

Package Manager Telephony Manager

Resource Manager Location Manager

...

Surface Manager Media Framework SQLite

OpenGL|ES Freetype WebKit

SGL SSL Sibc

ANDROID RUNTIME

Core Libraries

Dalvik Virtual Machine

Display Driver Camera Driver Bluetooth Driver Shared Memory Drivers

Binder (IPC) Driver

USB Driver Keypad Driver WiFi Driver Audio Drivers

Power Management

그림 1-1 | 안드로이드의 구조

01안드로이드 프레임워크 개요

Page 20: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

2 l 인사이드 안드로이드

그림 1-1은 안드로이드 플랫폼의 구조를 나타낸 것으로 크게 애플리케이션 프레임워크, 라이브러

리, 안드로이드 런타임, 리눅스 커널로 구성돼 있다.

여기서 주목할 점은 안드로이드에서 제공하는 기본 애플리케이션(Home, Camera, Dialer,

Browser 등)이 애플리케이션 프레임워크 레이어 위에서 동작하고 있다는 것이다. 마찬가지로 개발자

도 안드로이드 SDK를 통해 애플리케이션 프레임워크에서 제공하는 API를 이용하면 애플리케이션

프레임워크 위에서 동작하는 안드로이드 애플리케이션을 개발할 수 있다.

그렇다면 애플리케이션 개발자들이 이러한 안드로이드 프레임워크에 대해 심도 있게 이해할 필요

가 있을까?

앞에서도 언급했듯이 프레임워크를 제대로 이해하지 않더라도 얼마든지 안드로이드 애플리케이션

을 개발할 수 있다. 그러나 프레임워크의 구조에 익숙하고 그것의 동작 원리를 제대로 파악한다면 어

떠한 개발 시나리오라도 주눅들지 않고 안드로이드 프레임워크에 최적화된 소프트웨어를 설계할 수

있다는 자신감이 생길 것이다. 또한 이책에서 다루지는 않지만 안드로이드 프레임워크에서 기본적으

로 제공되는 AlarmClock, Contacts 등과 같은 애플리케이션의 소스 코드는 더 높은 수준의 안드로

이드 애플리케이션을 개발할 때 참고 자료로 활용할 수 있다.

반면 안드로이드 플랫폼 개발자라면 안드로이드 프레임워크에 대한 심도 있는 이해는 필수다. 안드

로이드는 오픈소스로 제공되므로 각 하드웨어 벤더는 기본 안드로이드 프레임워크를 커스터마이즈해

서 경쟁사와 차별화된 제품을 출시하고 있다. 그림 1-2는 안드로이드에서 제공하는 에뮬레이터의 기

본 UI를 비롯해서 각 제조사별로 커스터마이즈한 안드로이드 UI를 보여준다.

에뮬레이터 Froyo기본 UI HTC Sense UI Samsung Touchwiz UI Sony Ericsson Rachel UI

그림 1-2 | 안드로이드의 기본 UI 및 각 하드웨어 제조사별 안드로이드 UI 화면

Page 21: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

01 안드로이드 프레임워크 개요 l 3

이처럼 안드로이드 프레임워크를 바탕으로 차별화된 자사 솔루션을 구축하려면 안드로이드 프레

임워크에 대해 정확하고 심도 있게 분석할 필요가 있다.

1.1 안드로이드의소스코드구조

그렇다면 안드로이드 프레임워크를 어떻게 공부해야 할까? 안드로이드 프레임워크를 공부할 때 가장

좋은 자료는 역시 소스 코드다. 안드로이드는 오픈소스 프로젝트로서 http://android.git.kernel.org/

에서 관련 소스를 전부 내려받을 수 있다. 이와 관련된 내용은 2장을 참고하자.

안드로이드의 주요 소스 코드는 다음과 같이 구성돼 있다.

▒ kernel : 안드로이드의 리눅스 커널 2.6

▒ bionic : 안드로이드 표준 C 라이브러리

▒ bootloader : 참고용 안드로이드 부트로더

▒ build : 안드로이드 빌드 시스템

▒ cts : 안드로이드 호환성 테스트 관련 소스

▒ dalvik : 달빅 가상 머신

▒ external : 안드로이드에서 사용하는 오픈소스들

▒ frameworks : 안드로이드 프레임워크

▒ hardware : 안드로이드 HAL (Hardware Abstraction Layer) 소스

▒ packages : 안드로이드 기본 애플리케이션, 컨텐트 프로바이더 등

▒ system : 안드로이드 init 프로세스, 블루투스 도구 모음 등

이 책에서는 이 가운데 kernel, frameworks, packages, system 폴더에 든 소스 코드를 주로 분석하

겠다. 이외에도 안드로이드 프레임워크를 좀더 심도 있게 분석하고 싶은 독자라면 특히 frameworks

폴더에 들어있는 다른 소스 코드를 우선적으로 분석해보기 바란다(이 책에서는 특정 안드로이드 프

레임워크 소스를 설명할 때 해당 코드의 파일 경로를 각주로 달았다).

1.2 부팅프로세스로알아보는안드로이드프레임워크

안드로이드의 전체 소스 코드의 양은 엄청나다(필자의 경우 안드로이드 2.2를 기준으로 리눅스 코드

를 제외한 내려받은 관련 소스 코드의 크기가 4GB를 넘었다). 따라서 이처럼 거대한 안드로이드를 제

Page 22: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

4 l 인사이드 안드로이드

대로 파악하려면 엄청난 시간과 노력이 필요할 것이다. 하지만 아직까지도 안드로이드 프레임워크를

일목요연하게 설명한 자료는 없는 상태다.

그럼 안드로이드 프레임워크를 분석하려면 어떤 방법이 좋을까? 답은 생각보다 간단하다. 우리는 다

른 사람의 프로그램을 분석할 때 보통 프로그램의 최초 시작점인 main() 함수부터 차근차근 따라가

면서 해당 프로그램의 흐름과 동작을 파악하는 데에 익숙하다. 마찬가지로 이 책에서도 복잡하면서도

거대한 안드로이드 프레임워크를 분석하기 위해 안드로이드 플랫폼의 시작점인 부팅 과정에 주목했

다. 리눅스 커널의 시작부터 홈(Home) 애플리케이션까지 이어지는 안드로이드 부팅 과정을 순차적으

로 분석하다 보면 좀더 체계적으로 안드로이드 프레임워크의 동작 원리를 이해할 수 있을 것이다.

그림 1-3은 이러한 안드로이드의 부팅 과정을 간단한 그림으로 나타낸 것이다. 이 책에서는 앞으로

여러 장에 걸쳐 이 같은 부팅 과정을 바탕으로 안드로이드 프레임워크가 어떻게 초기화되고, 부팅 과

정에 참여하는 모듈끼리 어떻게 상호작용하는지를 중점적으로 분석하겠다.

리눅스 커널

init

initinitDaemons Context Manager Media Server Zygote

System Server

Activity Manager Service

Connectivity Service

Location Service

Power Manager Service

Sensor Service

자바 시스템 서비스

Audio Flinger

Camera Service

네이티브 시스템 서비스

자바 기반 프로세스

C/C++ 기반 프로세스

USB 데몬디버거 데몬

...

네이티브 서비스 등록(바인더 IPC)

자바 시스템 서비스 등록(JNI + 바인더 IPC)

(3) (4) (5)

(1)

(2)

(6)

이 책에서 다루는 범위

그림 1-3 | 안드로이드의 부팅 과정

Page 23: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

01 안드로이드 프레임워크 개요 l 5

본격적인 내용에 들어가기에 앞서 부팅 과정에 대해 간단히 알아보자.

(1) 리눅스 커널

안드로이드는 리눅스 기반의 플랫폼이다. 따라서 부팅 시에 부트로더를 통해 리눅스 커널이 먼저 시

작된다. 리눅스가 부팅되면 일반적인 리눅스 부팅 과정처럼 커널 초기화를 수행한 후 마지막 과정에서

init 프로세스를 호출한다.

(2) init

안드로이드 init 프로세스는 각종 디바이스를 초기화하는 작업을 비롯해서 안드로이드 프레임워크

동작에 필요한 각종 데몬, 컨텍스트 매니저(Context Manager), 미디어 서버(Media Server), Zygote

등을 실행하는 역할을 한다.

다음은 init 프로세스가 실행하는 데몬 프로세스다.

▒ USB 데몬(usbd) : USB 연결 관리

▒ 안드로이드 디버그 브리지 데몬(adbd) : 안드로이드 디버그 브리지(ADB) 연결 관리

▒ 디버거 데몬(debuggerd) : 디버그 시스템 시작

▒ 무선 인터페이스 레이어 데몬(rild) : 무선 통신 연결 관리

(3) 컨텍스트 매니저

컨텍스트 매니저(Context Manager)는 안드로이드의 시스템 서비스를 관리하는 중요한 프로세스다. 시

스템 서비스는 안드로이드 프레임워크를 구성하는 중요한 컴포넌트로서 카메라, 오디오, 비디오 처리

에서부터 각종 애플리케이션 제작에 필요한 중요 API를 제공하는 등의 역할을 수행한다.

안드로이드 내에서 동작하는 각종 시스템 서비스에 대한 정보는 컨텍스트 매니저에게서 얻을 수 있

다. 따라서 시스템 서비스를 이용하고자 하는 애플리케이션이나 프레임워크의 내부 모듈은 이를 서

비스 매니저에게 요청해야 한다. 요청 후에는 바인더(Binder)라는 안드로이드의 자체적인 IPC(Inter-

process communication) 메커니즘을 통해 시스템 서비스를 이용할 수 있다.

이를 위해서는 안드로이드의 모든 시스템 서비스는 부팅 시 자신의 핸들 정보를 컨텍스트 매니저에

등록해야 하며, 이러한 서비스 등록 과정에서도 프로세스 간 통신을 수행하기 위해 바인더 IPC가 이

용된다.

Page 24: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

6 l 인사이드 안드로이드

(4) 미디어 서버

미디어 서버(Media Server) 프로세스는 안드로이드에서 Audio Flinger(오디오 출력을 담당)나 Camera

서비스와 같이 C/C++ 기반으로 작성돼 있는 네이티브 시스템 서비스를 실행하는 역할을 한다.

(5) Zygote

Zygote는 안드로이드 애플리케이션의 로딩 시간을 단축하기 위한 프로세스로서 모든 자바 기반 안드

로이드 애플리케이션은 Zygote를 통해 포크(fork)된 프로세스 상에서 동작한다.

(6) 시스템 서버

시스템 서버(System Server)는 Zygote에서 최초로 포크되어 실행되는 안드로이드 애플리케이션 프로

세스다. 시스템 서버는 애플리케이션 생명 주기를 제어하는 액티비티 매니저 서비스(Activity Manager

Service)나 단말기의 위치 정보를 제공하는 로케이션 매니저 서비스(Location Manager Service)와 같은

자바 시스템 서비스를 실행하는 역할을 한다.

이처럼 시스템 서버에서 실행하는 자바 시스템 서비스도 안드로이드 애플리케이션이나 프레임워크

내부 모듈에서 이용할 수 있게 하기 위해서는 컨텍스트 매니저에 등록돼 있어야 한다.

그런데 바인더 IPC를 통해 자바 시스템 서비스를 C 언어 기반의 서비스 매니저에 등록하려면 자바

와 C 언어 간의 인터페이스 역할을 하는 JNI(Java Native Interface)를 추가로 이용해야 한다.

이상으로 안드로이드 부팅 과정에서 프레임워크가 초기화되는 부분을 간단히 알아봤다. 물론 안드

로이드의 부팅 과정이 여기서 끝나는 것은 아니다. 그림 1-3처럼 모든 자바 시스템 서비스가 로딩되고

나면 액티비티 매니저 서비스가 HOME 애플리케이션을 실행하면서 계속 부팅 과정이 진행된다. 이후

과정은 이 책의 범위를 벗어나므로 관심이 있는 독자는 안드로이드 소스 코드를 계속해서 분석해 보

기 바란다.

비록 이 책에서는 지면상의 이유로 전체 안드로이드 프레임워크 모듈의 세부 동작까지는 소개하지

않겠지만 이 책의 내용을 바탕으로 필요한 부분과 관련된 안드로이드 소스 코드를 분석한다면 훨씬

더 수월하게 안드로이드 프레임워크를 파악할 수 있을 것이다.

Page 25: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

7

이번 장에서는 안드로이드 플랫폼 소스를 빌드하고 SDK 개발 도구와 에뮬레이터 설치 및 애플리케이

션 프레임워크를 디버깅하는 방법을 설명한다. 먼저 기본적인 호스트 환경 구성을 마친 후 안드로이

드 플랫폼 빌드 환경과 SDK 개발 환경을 구축하는 등 기본 환경을 구성하는 작업을 진행하겠다. 이어

서 Hello World 어플리케이션을 이용하여 안드로이드 애플리케이션 프레임워크를 소스 수준에서 디

버깅하는 과정을 살펴봄으로써 안드로이드 애플리케이션 프레임워크의 동작 과정을 간략하게 살펴

보는 것으로 이번 장을 마무리하겠다.

2.1 호스트환경구성

안드로이드 개발 환경은 다양하지만 이 책에서는 리눅스 패키지 가운데 우분투 리눅스1 상에서 안드

로이드 플랫폼을 빌드하고 에뮬레이터를 구동하고 안드로이드 애플리케이션 프레임워크를 디버깅하

는 방법을 알아보겠다. 하지만 일반적으로 널리 쓰는 마이크로소프트 윈도우 XP와 같은 운영체제를

쓰고 있다면 VirtualBox라는 가상 머신에 우분투를 설치해서 개발 환경을 구축할 수 있다.

여기서는 윈도우 XP를 사용하고 있는 독자들을 위해 VirtualBox 가상 머신에 우분투를 설치하는

방법을 간략히 소개한 후, 안드로이드 플랫폼의 소스를 내려받아 빌드해 보겠다.

1  안드로이드 공식 사이트에서는 안드로이드 소스 코드를 내려 받고 컴파일 할 때 우분투 리눅스를 예로 들어 설명하고 있다. http://

source.android.com/source/download.html

02안드로이드 개발 환경 구축

Page 26: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

8 l 인사이드 안드로이드

2.1.1 VirtualBox 설치

VirtualBox는 무료로 사용할 수 있는 가상화 소프트웨어로 오라클에서 제공하는 오픈소스 프로

그램이다. VirtualBox는 버전업이 빠르며 다른 가상 머신과 비교해 상당히 가볍고 속도도 빠르다.

VirtualBox는 아래 사이트에서 내려받을 수 있다.

http://www.virtualbox.org/wiki/Downloads

그림 2-1 | VirtualBox 설치 화면

VirtualBox를 성공적으로 설치하고 난 후에 VirtualBox를 실행하여 새로 만들기(Ctrl+N) 아이콘

을 클릭하여 가상 머신을 생성한다. 그림 2-2와 같이 가상 머신을 생성하는 화면이 나타나면 운영체

제 종류에 Linux와 Ubuntu를 선택한다. 가상 머신을 생성할 때 메모리와 하드 디스크의 용량을 충

분히 확보해야 한다. 메모리의 경우는 1.5GB 이상을 권장하며, 안드로이드 소스를 빌드하기 위해서는

10GB 이상의 공간이 필요하므로 하드 디스크의 용량은 그 이상을 확보하도록 하자.

VirtualBox를 이용해 개발 환경을 구축한 후 이를 VirtualBox 이미지 형태로 저장해 두면 다른 호

스트 PC에서도 VirtualBox만 설치하면 안드로이드 개발 환경을 그대로 사용할 수 있다는 장점이 있다.

Page 27: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 9

그림 2-2 | 가상머신 생성 화면

2.1.2 우분투 설치

우분투는 가장 널리 알려진 데스크탑용 리눅스 패키지로 아래 사이트에서 다운받을 수 있다.

http://www.ubuntu.com/desktop/get-ubuntu/download

그림 2-3 | VirtualBox의 설정 화면(저장소)에서 Ubuntu 이미지를 선택

VirtualBox의 머신 > 설정 메뉴를 클릭하면 생성된 가상머신의 설정 창이 나타난다. 화면 왼쪽의

‘저장소’를 선택하고 IDE 컨트롤러의 속성에서 ‘CD/DVD 장치’ 항목을 다운로드 받은 우분투 이미지

파일2 로 설정한다.

2 ubuntu-10.04-desktop-i386.iso

Page 28: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

10 l 인사이드 안드로이드

우분투란?

우분투라는 말은 남아프리카에서 사용하는 반투어로 “네가 있기에 내가 있다”는 타인을 향한 인류애를 의미

한다. 우분투는 6개월 단위로 새로운 버전이 발표되며 Ubuntu 명칭 뒤에 있는 숫자의 의미는 발표된 년과

달을 뜻한다. 2010년 7월 현재 최종적으로 확인한 우분투의 최신버전은 Ubuntu 10.04 LTS(Long Term

Support)이다. LTS 버전은 2년에 한번씩 버전이 발표되며 장기간 지원되는 버전이다. 데스크탑 버전의 경우

에는 3년, 서버는 5년간 기술지원이 된다.

2.2 안드로이드플랫폼빌드환경구축

지금까지 안드로이드 플랫폼을 개발하기 위한 호스트 PC를 구성하는 방법을 소개했다. 다음으로 안

드로이드 플랫폼 빌드에 필요한 빌드 유틸리티 및 안드로이드 플랫폼의 소스 코드를 내려받는 방법을

설명하겠다.

2.2.1 빌드 유틸리티

안드로이드 플랫폼을 우분투 리눅스(32-bit x86환경)에서 빌드하려면 먼저 아래와 같은 패키지가 리

눅스에 설치돼 있어야 한다.

1. Git 1.5.4 버전 이상, GNU Privacy Guard.

2. JDK 5.0, update 12 버전 이상 (@override 어노테이션 문제로 JDK 6.0은 지원하지 않는다.)3

JDK 5버전이 아닌 경우 소스 컴파일 시 아래와 같은 에러가 발생한다.

You are attempting to build with the incorrect version of java.Your version is: java version "1.6.0_15".The correct version is: 1.5.Please follow the machine setup instructions at http://source.android.com/download

3. «ex, bison, gperf, libsdl-dev, libesd0-dev, libwxgtk2.6-dev (optional), build-essential, zip, curl.

위의 유틸리티는 우분투에서 apt 유틸리티를 이용하여 설치 가능하다. 리눅스 셸 프롬프트에서 다

음과 같이 명령을 입력하자.

3  @override 어노테이션은 부모 클래스나 인터페이스의 메서드를 재정의한다는 것을 컴파일러에게 명시적으로 알려준다. 이 표시는

Java 1.5에서 부모 클래스에 대해서만 지원하기 때문에, 인터페이스의 메서드 구현에 이 표시가 있으면 에러가 발생한다. 프로요가

발표되고 나서 Java 5와 Java 6 중에서 어떤 버전이 자바 컴파일러인지 논의가 있었으며, 오픈소스 릴리스에 관여하고 있는 구글의

Jean-Baptiste Queru 말에 따르면 1.5가 내부적으로 프로요를 개발하고 테스트한 버전이라고 한다.

Page 29: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 11

$ sudo apt-get install git-core gnupg flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl libncurses5-dev zlib1g-dev4

필요하다면 valgrind를 설치한다. 이는 메모리 누수, 스택 오버플로우 등을 찾는 데 도움이 된다.

$ sudo apt-get install valgrind

우분투 intrepid (8.10) 버전 사용자는 libreadline 최신 버전이 필요할 수도 있다.

$ sudo apt-get install lib32readline5-dev

이제 자바를 설치하자. 우분투 10.04의 소프트웨어 소스 저장소에는 자바 1.6 버전만 지원하므로

자바 1.5 버전을 설치하려면 다음의 절차대로 진행하면 된다.

1. 저장소 추가하기

$ sudo add-apt-repository "deb http://us.archive.ubuntu.com/ubuntu/ jaunty multiverse"$ sudo add-apt-repository "deb http://us.archive.ubuntu.com/ubuntu/ jaunty-updates multiverse"

2. 패키지 인덱스 업데이트

$ sudo apt-get update

3. 자바 1.5 버전 설치하기

$ sudo apt-get install sun-java5-jdk

4. 설치된 모든 자바 버전 확인

$ sudo update-java-alternatives -l

5. 만약 여러 개의 자바 버전이 설치되어 있다면 자바 1.5 버전으로 변경

$ sudo update-java-alternatives -s java-1.5.0-sun

6. 자바 버전 확인

$ java -versionjava version "1.5.0_19"Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_19-b02)Java HotSpot(TM) Server VM (build 1.5.0_19-b02, mixed mode)

JDK를 설치 후 JAVA_HOME 환경 설정을 해야 한다. 자바가 설치된 디렉토리 정보는 쉘 프롬프트

에서 ¬nd /usr -name "java-1.5*" 을 입력하면 알 수 있다.

$ export JAVA_HOME=/usr/lib/jvm/java-1.5.0-sun$ export ANDROID_JAVA_HOME=$JAVA_HOME$ export PATH=$JAVA_HOME/bin:$PATH

4 zlib1g-dev: 중간에 1은 알파벳 l이 아닌 숫자 1이다.

Page 30: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

12 l 인사이드 안드로이드

2.2.2 Repo 설치

안드로이드의 방대한 소스 코드는 Git라는 소스 버전 관리 도구를 이용하여 프로젝트 단위로 분산되

어 개발이 진행된다. 이처럼 독립적으로 개발이 진행되는 각 모듈을 안드로이드 소스 코드를 배포할

때 하나의 버전으로 만드는 작업을 Repo 스크립트를 이용하면 손쉽게 할 수 있다.

먼저 curl 유틸리티를 이용해서 Repo 스크립트를 내려받은 후 실행권한을 변경한다.

$ cd ~$ mkdir bin$ export PATH=$PATH:~/bin$ curl http://android.git.kernel.org/repo >~/bin/repo$ chmod a+x ~/bin/repo

2.2.3 안드로이드 플랫폼의 소스 코드 내려받기

Repo를 내려받은 후 아래 명령으로 안드로이드 소스 코드의 배포 정보가 담긴 manifest.git 파일

을 내려받는다. -b 옵션 뒤의 버전명을 기술하면 해당 버전의 소스를 다운로드할 수 있다. 예를 들어

froyo는 프로요 버전의 manifest.git 파일을 내려받는다는 의미이며, -b를 빼면 현재 메인 버전의 안

드로이드 소스를 내려받게 된다.5

$ cd ~$ mkdir mydroid$ cd mydroid $ repo init -u git://android.git.kernel.org/platform/manifest.git –b froyo-plus-aosp

위와 같이 입력하면 froyo 브렌치와 관련된 소스 정보들이 출력되고, 사용자 정보를 입력하라는 메

시지가 화면에 표시되면 이름과 이메일 주소를 차례로 입력하도록 하자. 명령의 실행이 종료된 후 디

렉토리 정보를 출력하면 아래와 같이 .repo 디렉토리가 생성된 것을 볼수 있다.

android@android-desktop:~/mydroid$ ls -altotal 80drwxr-xr-x 19 android android 4096 2010-06-29 20:08 .drwxr-xr-x 30 android android 4096 2010-06-29 20:08 ..drwxr-xr-x 6 android android 4096 2010-06-29 17:11 .repo

마지막으로 repo sync 명령으로 안드로이드 플랫폼 소스를 내려받는다. 전체 소스의 크기가 2GB

가 넘기 때문에 내려받는 데 시간이 오래 걸릴 수 있음을 참고하기 바란다.

$repo sync

5 2011년 2월 현재 최신 버전의 프로요 버전을 받을려면 -b 옵션 뒤에 android-2.2.2_r1을 입력하면 된다. 최신 버전의 정보는 http://

android.git.kernel.org/?p=platform/frameworks/base.git;a=summary에 접속하여 tags 혹은 heads를 확인해 본다.

Page 31: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 13

2.2.4 안드로이드 플랫폼 빌드

커널을 제외한 안드로이드 플랫폼 소스를 모두 내려받았으면 이제 시스템을 빌드할 수 있다. 실제 타

겟에 포팅하려면 설정을 많이 바꿔야겠지만 에뮬레이터 환경을 위한 시스템 빌드는 기본 설정만 가지

고 컴파일을 진행하면 된다. 컴파일은 안드로이드 소스의 최상위 디렉토리에서 다음과 같이 make 명

령을 실행하면 된다.

$ cd ~/mydroid $ make

필자의 빌드 환경에서는 그림 2-4와 같은 결과가 나왔다.

그림 2-4 | 안드로이드 플랫폼을 성공적으로 빌드한 후 출력된 로그 메시지

2.3 안드로이드SDK개발환경구축

지금까지 안드로이드 플랫폼의 빌드 환경을 구성하고 Git 서버로부터 소스 코드를 다운로드하여 빌

드하는 과정을 설명하였다. 다음으로 안드로이드 애플리케이션을 개발하고 디버깅하기 위해 필요한

이클립스, 안드로이드 SDK, ADT 이클립스 플러그인을 설치하는 과정을 설명하겠다.

Page 32: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

14 l 인사이드 안드로이드

2.3.1 이클립스 개발 환경 구축

이번에는 그림 2-5의 이클립스 다운로드 사이트(http://www.eclipse.org/downloads/)에서

‘EclipseIDE for Java Developers’ 버전을 내려받아 설치한다.

그림 2-5 | 이클립스 다운로드 웹 페이지 화면

이클립스를 내려받았다면 압축을 풀고 이클립스를 실행한다.

$ tar –xvzf eclipse-java-helios-SR1-linux-gtk.tar.gz$ cd eclipse$ ./ eclipse &

2.3.2 안드로이드 SDK starter 패키지 다운로드

리눅스용 안드로이드 SDK starter 패키지를 안드로이드 개발자 사이트(http://developer.android.

com/sdk/index.html)에서 내려받은 후 압축을 푼다.

Page 33: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 15

그림 2-6 | 안드로이드 SDK 다운로드 웹페이지 화면

2.3.3 이클립스용 ADT 플러그인 설치

이클립스에 ADT(Android Development Tool)을 설치하자.

그림 2-7 | 안드로이드 ADT 설치(1/4)

그림 2-7과 같이 이클립스의 Help > Install New So°ware 메뉴를 클릭하고 그림 2-8과 같이 Add

버튼을 클릭해서 ADT 플러그인이 위치한 경로(https://dl-ssl.google.com/android/eclipse)를 입력

한다.

Page 34: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

16 l 인사이드 안드로이드

그림 2-8 | 안드로이드 ADT 설치 화면(2/4)

그림 2-9와 같이 DDMS와 ADT가 확인되면 Next를 눌러 설치를 진행한다.

그림 2-9 | 안드로이드 ADT 설치 화면(3/4)

라이센스 정책을 모두 동의하면 그림 2-10과 같이 설치가 진행된다.

Page 35: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 17

그림 2-10 | 안드로이드 ADT 설치 화면(4/4)

설치가 완료되면 이클립스를 재시작한다.

2.3.4 안드로이드 SDK 경로 설정

이클립스를 재시작한 후에 내려받은 안드로이드 SDK의 설치 경로를 이클립스에 설정하기 위해

Windows > Preferences 메뉴를 클릭한다.

그림 2-11 | 이클립스 SDK 경로 설정(1/2)

Page 36: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

18 l 인사이드 안드로이드

Preferences 창 왼쪽에 Android라는 메뉴가 새롭게 추가된 것을 확인할 수 있다. SDK Location에

2.3.2절에서 압축을 푼 Android SDK starter 패키지의 경로를 지정한다.

그림 2-12 | 이클립스 SDK 경로 설정(2/2)

2.3.5 안드로이드 SDK 설치

안드로이드 SDK starter 패키지에는 안드로이드 애플리케이션을 개발하는 데 사용되는 몇몇 도구만

포함되어 있고, SDK 버전별로 안드로이드 클래스 라이브러리, 플랫폼 에뮬레이터, 유용한 예제 프로

그램, 참고문서 등이 포함되어 있지 않다. 이와 같은 정보는 안드로이드 애플리케이션 개발에 필수적

인 것이므로 이클립스 ADT 플러그인에 포함된 Android SDK and AVD Manager 도구를 이용하여

안드로이드 SDK에 추가해야 한다.

이클립스의 Windows > Android SDK and AVD Manager 메뉴를 클릭하여 Android SDK and

AVD Manager 창을 띄운다.

그림 2-13 | 이클립스 메뉴에서 Android SDK and AVD Manager 실행하기

Page 37: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 19

그림 2-14 | 안드로이드 SDK에 추가할 항목 선택

그림 2-14의 왼쪽에서 Available Package 메뉴를 선택하면 오른쪽 창에 안드로이드 SDK에 추가할

항목이 표시된다. 추가할 항목을 선택 후 설치 버튼을 클릭한다. 추가할 항목은 안드로이드 플랫폼 버

전별 SDK, 구글 API, 샘플코드, 문서 등이며, 내려받을 항목을 선택 후 설치 버튼을 클릭한다.

라이센스 정책을 모두 동의하면 그림 2-15와 같이 설치가 진행된다.

그림 2-15 | 선택된 항목 내려받기

Page 38: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

20 l 인사이드 안드로이드

2.4 안드로이드애플리케이션개발

2.3절에서 이클립스, 이클립스용 ADT 플러그인, 안드로이드 SDK를 설치하는 것으로 안드로이드 애

플리케이션 개발을 위한 개발 환경 구성이 끝났다. 이제 정상적으로 개발 환경이 구축됐는지 확인하

기 위해 Hello 애플리케이션을 작성해서 에뮬레이터에서 실행해보자.

2.4.1 Hello 애플리케이션 작성

Hello 애플리케이션을 작성하기 위해 Android 프로젝트를 만든다. 그림 2-16과 같이 이클립스 메뉴

에서 File > New > Project > Android Project를 선택한다.

그림 2-16 | 안드로이드 프로젝트 생성

그림 2-17과 같이 프로젝트 정보를 입력한 후 Finish를 클릭한다. 이클립스 화면 좌측의 Package

Explorer를 살펴보면 HelloWorld 프로젝트가 생성된 것을 확인할 수 있다.

코드 2-1은 이클립스에서 자동으로 생성한 안드로이드 애플리케이션의 코드이다.

package org.example.hello;

import android.app.Activity;import android.os.Bundle;

Page 39: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 21

public class HelloWorld extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); }}

코드 2-1 | HelloWorld.java 소스 코드

그림 2-17 | 안드로이드 애플리케이션 프로젝트 생성 창

Page 40: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

22 l 인사이드 안드로이드

HelloWorld 애플리케이션을 실행하면 그림 2-18과 같이 애플리케이션이 실행될 안드로이드 디바

이스가 없다는 오류가 발생한다.

그림 2-18 | 애플리케이션이 실행될 에뮬레이터가 없어서 발생하는 오류

가상의 안드로이드 디바이스를 만들기 위해 Window > Android SDK and AVD Manager 메뉴를

클릭한다. 다음으로 그림 2-19에 나온 바와 같이 에뮬레이터를 생성하는 데 필요한 설정 값을 입력한

후, Create AVD 버튼을 클릭하면 안드로이드 가상 머신이 만들어진다.

그림 2-19 | Create new AVD 창

Page 41: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 23

이클립스 좌측의 Package Explorer 창에서 HelloWorld 프로젝트를 선택한 후 마우스 오른쪽 버튼

을 클릭하면 Run as 메뉴가 보인다. 여기서 Android Application을 선택하면 에뮬레이터가 실행되면서

HelloWorld 애플리케이션이 실행된다. 그림 2-20은 HelloWorld 애플리케이션이 실행된 화면이다.

그림 2-20 | HelloWorld 애플리케이션 실행 화면

안드로이드 애플리케이션 개발에 관한 설명은 이 책의 범위를 벗어나므로 생략하도록 하겠다.

2.5 애플리케이션프레임워크소스레벨디버깅

안드로이드 플랫폼은 C/C++로 개발된 리눅스 드라이버, 네이티브 라이브러리, 자바로 개발된 애플리

케이션 프레임워크, 애플리케이션의 네 부분으로 구성돼 있다. 안드로이드 플랫폼 코드를 개발하다

보면 작성된 프로그램을 디버깅해야 하는 경우가 빈번히 발생한다. 따라서 이번 장에서는 자바로 개

발한 코드를 애플리케이션 프레임워크 소스 레벨에서 디버깅하는 방법을 설명하겠다.

2.5.1 애플리케이션 프레임워크 소스 로딩

시스템 소스를 디버깅하려면 먼저 시스템 소스를 이클립스에 불러와야 한다. 그러나 안드로이드 플랫

폼 디렉토리에는 수많은 프레임워크 소스가 개별적으로 존재하기 때문에 이클립스에 소스를 모두 불

러오려면 노력이 많이 든다. 다행히 안드로이드에는 프레임워크의 소스 경로를 모두 모아 놓은 이클립

스 설정 파일이 포함돼 있으므로 아래 절차에 따라 이클립스에 프레임워크 소스를 불러온다.

Page 42: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

24 l 인사이드 안드로이드

1. 2.2절에서 설명한 것처럼 안드로이드 빌드 환경이 구성돼 있어야 한다. 빌드 환경을 성공적으

로 구성하고 나면 make 명령으로 안드로이스 시스템을 빌드한다.

2. 안드로이드에서 제공하는 .classpath 파일을 안드로이드 플랫폼 소스 디렉토리의 최상단으로 복사한다.

$ cd mydroid$ cp development/ide/eclipse/.classpath ~/mydroid$ chmod u+w .classpath

3. 이클립스를 실행하고 Java Project를 생성한다. 그림 2-21과 같이 Location 항목에서 디렉토리

를 안드로이드 소스가 설치된 최상위 디렉토리로 설정한 후(2번 항목에서 .classpath 파일을 복사한 곳으로 설정) Finish 버튼을 클릭한다.

4. Package Explorer 창을 보면 그림 2-22와 같이 안드로이드 프레임워크의 소스가 등록돼 있음

을 확인할 수 있다.

그림 2-21 | 자바 프로젝트 생성 시 안드로이드 프레임워크 소스 경로를 지정

Page 43: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 25

그림 2-22 | 프로젝트로 불러온 안드로이드 프레임워크의 소스

프로젝트 생성 시 아래와 같은 에러가 발생한다면,

Description Resource Path Location Type

Project 'Generic-Android' is missing required library: 'out/target/

common/obj/JAVA_LIBRARIES/google-common_intermediates/javalib.jar'

Generic-Android Build path Build Path Problem

Description Resource Path Location Type

Project 'Generic-Android' is missing required library: 'out/target/

common/obj/JAVA_LIBRARIES/gsf-client_intermediates/javalib.jar'

Generic-Android Build path Build Path Problem

구글 "android-platform" 그룹스에서 윈드리버의 Quiring, Sam의 답변을 참고하자.

http://groups.google.com/group/android-platform/browse_thread/thread/5c86d1f1929eed3c

Page 44: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

26 l 인사이드 안드로이드

5. 안드로이드 플랫폼 소스를 디버깅하기 위해 디버거를 설정한다. 그림 2-23과 같이 HelloWorld 프로젝트에서 마우스 오른쪽 버튼을 클릭한 후 Debug As > Debug Con¬gurations 메뉴를 선택한다.

그림 2-23 | 안드로이드 플랫폼 디버깅을 위한 디버그 설정

6. 그림 2-24에서 좌측 하단의 Remote Java Application 항목에서 마우스 오른쪽 버튼을 클릭한 후 New 메뉴를 선택한다. 오른쪽에 나타나는 디버그 설정창에서 그림 2-24와 같이 입력한 후 Apply 버튼을 눌러 변경 사항을 적용한다.

Page 45: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 27

그림 2-24 | Remote Java Application 디버거 설정 창

2.5.2 HelloWorld 프레임워크 소스 레벨 디버깅

지금까지 안드로이드 애플리케이션 프레임워크를 디버깅하기 위한 준비 작업이 완료되었다. 이제부터

는 HelloWorld 애플리케이션을 디버깅하면서, 애플리케이션과 관련된 프레임워크의 소스 코드를 직

접 따라가면서 변수 값을 확인해 보도록 하자.

1. HelloWorld 애플리케이션의 시작 지점에 브레이크 포인트를 설정하고 HelloWorld 프로젝트

에서 마우스 오른쪽 버튼을 클릭한 후 Debug As > Android Application을 클릭한다. 그러고 나면 그림 2-25와 같이 브레이크 포인트를 설정한 곳에서 실행이 멈추고 제어권이 이클립스의 자바 디버거에게 넘어와 디버깅을 할 수 있게 된다.

Page 46: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

28 l 인사이드 안드로이드

그림 2-25 | HelloWorld 애플리케이션 디버깅 화면

2. 애플리케이션 프레임워크를 소스 수준에서 디버깅하기 위해 그림 2-26에서 화면 좌측 상단의

Debug 창에 보이는 Activity�read 클래스를 선택한다. 그림 2-26처럼 소스를 발견하지 못했

다는 에러가 뜨면 Activity�read.performLaunchActivity 항목에 마우스 오른쪽 버튼을 클

릭한 후 Edit Source Lookup 메뉴를 선택한다.

그림 2-26 | 애플리케이션 프레임워크 디버깅 중 소스를 발견하지 못했다는 에러가 발생한 화면

Page 47: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 29

3. 그림 2-27은 Edit Source Lookup Path 설정 창인데 여기서 Add 버튼을 클릭한다.

그림 2-27 | Edit Source Lookup Path 설정 창

4. 그림 2-28에서 Java Project를 선택 후 2.5.1절에서 생성한 android-framework 프로젝트를 선택한다.

그림 2-28 | Add Source 창

Page 48: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

30 l 인사이드 안드로이드

5. 그림 2-29와 같이 ActivityThread 클래스의 소스가 보이면 성공한 것이다. 오른쪽 상단의

variables 창을 보면 애플리케이션 프레임워크의 모든 변수를 살펴볼 수 있다.

그림 2-29 | ActivityThread 클래스를 소스 레벨에서 디버깅

Page 49: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

02 안드로이드 개발 환경 구축 l 31

2.6 정리

지금까지 안드로이드 플랫폼을 빌드하고 애플케이션 개발을 위한 SDK 설치 및 애플리케이션 프레임

워크 디버깅 방법에 관해 설명하였다. 2장에서 사용한 주요 프로그램을 정리하면 다음과 같다.

▒ VirtualBox : Windows 계열의 운영체제에서 안드로이드 플랫폼 소스 코드를 빌드하기 위한 하나의 방법으로 리눅스를 설치하는데 사용되는 가상화 프로그램

▒ Ubuntu : 안드로이드 소스 코드를 빌드하기 위해 사용하는 리눅스 운영체제

▒ Git : 안드로이드 소스 코드 관리에 사용되는 분산 버전 관리 시스템

▒ Repo : 안드로이드 소스 코드를 손쉽게 다운로드하기 위해 사용한 파이썬 스크립트

▒ Eclipse : 안드로이드 애플리케이션 개발 및 디버깅에 사용한 개발 도구

▒ Android SDK : 안드로이드 애플리케이션 개발에 사용하는 프로그램 및 라이브러리 모음

▒ ADT Plugin for Eclipse : 안드로이드 애플리케이션 개발 작업을 돕는 유용한 이클립스 플러

그인

▒ development/ide/eclipse/.classpath : 안드로이드 애플리케이션 프레임워크 자바 소스 코드 및 컴파일된 클래스 파일 경로를 담고 있는 설정 파일

Page 50: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

32

Page 51: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

33

리눅스상에서 동작하는 모든 프로세스는 init 프로세스에서 생성되어 실행된다. 리눅스 커널이 부팅

하고 나면 사용자 영역에서 init 프로세스가 최초로 실행된 다음 시스템 동작에 필요한 다른 프로세

스들을 순차적으로 실행시킨다. 시스템 부팅이 완료된 이후, init 프로세스는 백그라운드 프로세스

로 동작하면서 다른 프로세스를 감시한다. 만약 감시중인 프로세스가 종료되어 좀비 상태가 되면 해

당 프로세스가 가진 자원이 정상적으로 반환되게 하는 역할을 수행한다. 안드로이드 플랫폼(이하 안

드로이드)의 init 프로세스는 일반적인 리눅스 상의 init 프로세스가 수행하는 기능 외에도 몇 가지 추

가적인 기능을 수행한다. 이 장에서는 안드로이드의 init 프로세스가 수행하는 특징적인 기능에 대해

알아보겠다. 본 장의 내용은 리눅스 시스템 프로그래밍에 대해서 어느 정도 지식이 있는 독자를 기준

으로 기술했으므로 시스템 프로그래밍에 생소한 독자는 관련 서적을 먼저 살펴보도록 한다.

3.1 init프로세스의실행과정

리눅스와 마찬가지로 안드로이드에서는 커널 부팅이 완료되면 최초의 사용자 프로세스인 init 프로세

스를 실행한다.

03init 프로세스

Page 52: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

34 l 인사이드 안드로이드

커널 부팅 과정

start_kemel()

init_post()

run_init_process() init 프로세스 실행

그림 3-1 | init 프로세스의 실행 과정

init 프로세스의 실행과정은 그림 3-1과 같이 start_kernel() 함수, init_post() 함수, run_init_

process() 함수가 순차적으로 실행되어 마지막 단계에서 init 프로세스를 실행한다. 코드 3-1은 커널

내부에서 구현된 init 프로세스를 실행하는 코드다.

static int noinline init_post(void){ if (execute_command) { ❶ run_init_process(execute_command); } run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh");}

코드 3-1 | init_post()1 를 통한 init 프로세스 실행

❶ init_post() 함수는 run_init_process() 함수를 호출하여 execute_command에 등록된 프로세

스 파일의 경로2를 가지고 execve() 시스템 콜을 호출한다. execve() 함수는 인자로 전달된 파일

경로의 프로세스를 실행한다. init 프로세스를 정상적으로 실행하기 위해서는 커널의 부팅 옵션

에서 “init=/init”과 같이 주어져야 한다. 이는 그림 3.2 에서 볼 수 있듯이 안드로이드 빌드 후에

생성되는 루트 파일 시스템에서 init 프로세스가 최상위 디렉터리에 존재하기 때문이다.

1  커널 코드의 init/main.c 에 정의된 함수이다.

2  커널의 부팅 옵션으로 정의돼 있다. 이 옵션은 커널의 make menuconfig 상에서 boot option에서 확인하거나, 커널 소스의 최상위 디

렉터리에 있는 .config 파일에서 “CONFIG_CMDLINE”을 통해 확인할 수 있다.

Page 53: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 35

init 프로세스

init.rc 설정 파일

그림 3-2 | 안드로이드 루트 파일 시스템의 내용

루트 파일 시스템의 최상위 디렉터리에 init 프로세스가 없거나 부팅 옵션에서 “init=”을 설정하지

않았다면 커널은 /sbin, /etc, /bin 디렉터리에서 init 파일을 찾는다. 해당 디렉터리에도 init 파일이 없

으면 커널이 init 프로세스를 실행하지 못해 커널 패닉이 일어난다.

코드 3-1에서 ❶의 과정을 정상적으로 수행하면 init 프로세스를 실행한다. 그렇다면 init 프로세스

가 하는 일은 무엇일까? init 프로세스가 하는 일을 파악하려면 실제 구현된 코드를 살펴보는 것이 낫

다. 다행이 안드로이드 플랫폼은 오픈소스로 제공되기 때문에 플랫폼 코드를 내려받아 init 프로세스

가 어떻게 동작하는지 살펴볼 수 있다. 다음 절에서는 init 프로세스의 소스 코드3를 기반으로 init 프

로세스의 동작 과정을 단계별로 살펴보겠다.

3.2 init프로세스의소스코드분석

이 절에서는 init 프로세스의 소스 코드 가운데 main() 함수에 기술된 내용을 순차적으로 분석하겠

다. 지면 관계상 소스 코드를 모두 기술하지는 못했으니 본 절을 읽는 독자는 init 프로세스의 실제 소

스 코드를 함께 살펴보도록 한다.

안드로이드의 init 프로세스는 다음과 같이 크게 4가지 기능을 수행한다.

3 http://android.git.kernel.org/?p=platform/system/core.git;a=tree에서 init 디렉터리 아래에서도 확인할 수 있다.

Page 54: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

36 l 인사이드 안드로이드

SIGCHLD 시그널 핸들러 등록(코드3-2)

디렉토리 생성 및 마운트(코드3-3)

init,rc 파일 파싱(코드3-7) 프로퍼티 초기화(코드3-12)

프로퍼티 초기 설정(코드3-14)

init 액션 리스트 실행(코드3-15)

프로퍼티 서비스 실행(코드3-16)

UDS를 위한 소켓 생성(코드3-17)

early-init 액션 리스트 실행(코드3-10)

early-boot & boot 액션 리스트 실행(코드3-18)

디바이스 정적 노드 생성(코드3-11)

init.{hardware}.rc 파일 파싱(코드3-9)

POLL 이벤트 등록(코드3-19)

POLL 이벤트 등록(코드3-19)

서비스 리스트 실행(3.3.4절)

POLL 이벤트 등록(코드3-19)

이벤트 처리 루프

디바이스 동적 노드 생성(3.4.2절)

자식 프로세스 재시작(3.5절)

프로퍼티 서비스 제공(3.6절)

디바이스 드라이버 노드 생성 자식 프로세스 종료 처리 프로퍼티 서비스init.rc 파일 분석 및 실행

POLL 대기(코드3-20)

그림 3-3 | init 프로세스의 주요 역할 및 이벤트 처리 루프의 동작

init 프로세스는 앞서 설명한 자식 프로세스의 종료 처리뿐만 아니라 애플리케이션이 디바이스 드

라이버에 접근할 때 사용하기 위한 디바이스 노드 파일을 생성하며, 시스템 동작에 필요한 환경 변수

를 저장하는 프로퍼티 서비스를 제공한다. 그리고 init 프로세스가 해야 할 일을 기술한 init.rc 파일을

분석해서 해당 파일에 담긴 내용에 따른 기능을 수행한다. 그림 3-3에서 나타낸 init의 각 단계별 주요

기능을 실제 소스 코드의 main() 함수를 순서대로 따라가면서 살펴보자. 그림 3-3의 사각박스에 나타

낸 동작들에 해당하는 코드의 위치나 상세한 설명이 있는 절을 표기하였으니 init 프로세스 분석 시

에 참고하기 바란다.

static int signal_fd = -1;static void sigchld_handler(int s){ write(signal_fd, &s, 1);}int main(int argc, char **argv){ struct sigaction act; act.sa_handler = sigchld_handler; act.sa_flags = SA_NOCLDSTOP; act.sa_mask = 0; act.sa_restorer = NULL; sigaction(SIGCHLD, &act, 0);

코드 3-2 | main() - SIGCHLD 시그널 핸들러 등록

Page 55: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 37

리눅스의 프로세스들은 서로 정보를 교환하기 위해 메시지를 주고 받는데, 이러한 메시지를 시그널

이라고 한다. 그리고 각 프로세스는 다른 프로세스에서 발생하는 시그널을 처리하기 위한 루틴을 등

록하는데 이를 시그널 핸들러라고 한다. 시그널에는 프로세스의 실행 상태가 변경되거나 종료됐을 때

발생하는 시그널이 있으며, 모든 프로세스의 조상인 init 프로세스는 자신이 생성한 프로세스가 종료

됐을 때 발생하는 SIGCHLD 시그널을 처리할 핸들러를 등록한다. 시그널 핸들러를 등록하기 위해서

는 시그널 등록 함수인 sigaction()4 함수에 sigaction 구조체의 내용을 채워 인자로 전달하면 된다.

init 프로세스는 코드 3-2를 통해 자신이 실행할 자식 프로세스에 대한 SIGCHLD 시그널 핸들

러를 등록하고 sigaction 구조체의 sa_«ags에 SA_NOCLDSTOP을 설정한다. 이 값은 프로세스가

종료되는 상황에서만 SIGCHLD 시그널을 받겠다는 의미다. 여기서 등록되는 핸들러인 sigchld_

handler는 전역으로 선언된 signal_fd 소켓에 SIGCHLD 시그널이 발생했음을 알리는 역할을 수행

한다. 발생한 시그널에 대한 실제 처리는 init 프로세스 동작 과정의 마지막 단계인 이벤트 처리 루프

에서 이뤄진다.

init 프로세스는 시그널 핸들러를 등록한 후 부팅에 필요한 디렉터리를 생성하고 마운트한다.

mkdir("/dev", 0755);mkdir("/proc", 0755);mkdir("/sys", 0755);

mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755"); 5

mkdir("/dev/pts", 0755);mkdir("/dev/socket", 0755);mount("devpts", "/dev/pts", "devpts", 0, NULL); 6

mount("proc", "/proc", "proc", 0, NULL); 7

mount("sysfs", "/sys", "sysfs", 0, NULL); 8

코드 3-3 | main() 디렉터리 생성 및 마운트

4  sigaction()은 시그널 처리를 결정하는 함수다. 이는 signal() 함수보다 개선된 형태로 struct sigaction 구조체를 인자로 사용하기 때문

에 좀더 다양한 옵션을 지정할 수 있다.

5  tmpfs는 램 파일 시스템의 일종으로 플래시나 하드디스크의 파일 시스템보다 접근 속도가 빠르다. /dev 디렉터리는 하드웨어 장치를

접근하기 위한 디바이스 드라이버가 존재하는 곳인데, 안드로이드는 해당 디렉터리를 tmpfs로 사용해 장치에 접근할 때 속도 향상

을 꾀했다.

6  devpts는 가상 터미널을 위한 파일 시스템이다.

7  커널 메모리에서 돌아가는 일종의 가상 파일 시스템이며 애플리케이션은 이 파일 시스템을 통해 커널 공간의 데이터에 접근할

수 있다.

8  sysfs 파일 시스템은 proc, devfs, devpts 파일 시스템을 하나로 통합한 파일 시스템 (Linux Kernel 2.6에서 도입)

Page 56: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

38 l 인사이드 안드로이드

안드로이드 빌드를 통해 생성되는 루트 파일 시스템에는 /dev, /proc, /sys와 같은 디렉터리가 존재

하지 않는다. 이러한 디렉터리는 시스템의 운용 중에만 필요한 디렉터리로 init 프로세스가 동작 중에

생성하고 시스템이 종료되면 다시 사라진다.

init 프로세스는 코드 3-3을 통해 시스템 운용에 필요한 디렉터리를 생성한다. 그림 3-4는 코드 3-3

을 통해 생성한 디렉터리들을 나타낸다. 그림에서 [ ]안의 내용은 해당 디렉터리가 마운트된 파일 시스

템을 나타낸다.

/

/dev[tmpfs]

/proc[proc]

/sys[sysfs]

/pts[devpts]

/socket

그림 3-4 | init이 생성한 디렉터리의 계층 구조

다음은 init 프로세스의 동작 메시지를 출력하기 위해 로그 장치를 생성하는 부분이다.

open_devnull_stdio();log_init(); INFO("reading config file\n");

코드 3-4 | main() - 로그 출력 장치 초기화

init 프로세스는 코드 3-3을 통해 디바이스 노드가 위치하는 /dev 디렉터리가 생성된 이후 open_

devnull_stdio() 함수를 통해 실행 로그를 출력하기 위한 장치를 생성한다. open_devnull_stdio()

함수는 /dev 디렉터리 이하에 __null__ 이라는 디바이스 노드 파일을 생성하고 표준 입력, 표준 출

력, 표준 에러 출력9 을 모두 __null__ 장치로 리다이렉션(redirection)한다.

9 유닉스 계열(리눅스 포함)은 모든 입력과 출력을 파일로 다룬다. 표준 입력은 0, 표준 출력은 1, 표준 에러는 2로 지정돼 있다.

Page 57: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 39

fd[0]

fd[1]

fd[2]

fd[3]

fd[...]

stdin

stdout

stderr

__null__

fd[0]

fd[1]

fd[2]

fd[3]

fd[...]

stdin

stdout

stderr

__null__X

그림 3-5 | open_devnull_stdio() 함수를 통한 로그 메시지 출력 장치 변경

그림 3-5는 open_devnull_stdio() 함수를 통해 입출력에 관련된 파일이 모두 __null__ 장치로 변

경되는 것을 보여준다. 따라서 open_devnull_stdio() 함수를 수행한 init 프로세스는 표준 입출력을

통해 메시지를 출력할 수 없다. 그렇다면 init 프로세스가 출력하는 로그 메시지는 볼 수 없는 것일까?

그렇지 않다. init 프로세스는 log_init() 함수를 통해 로그 메시지를 출력하기 위한 새로운 출력 장치

를 제공한다.

void log_init(void){ static const char *name = "/dev/__kmsg__"; if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { log_fd = open(name, O_WRONLY); fcntl(log_fd, F_SETFD, FD_CLOEXEC); unlink(name); }}

코드 3-5 | log_init() 함수를 통한 출력 장치 생성

init 프로세스는 log_init() 함수를 통해 “/dev/__kmsg__” 디바이스 노드 파일을 생성한다.

__kmsg__ 장치는 커널의 메시지 출력 함수인 printk() 함수를 사용하게 해주는데, init 프로세스는

이 장치를 통해 로그 메시지를 출력한다.

#define ERROR(x...) log_write(3, "<3>init: " x)#define NOTICE(x...) log_write(5, "<5>init: " x)#define INFO(x...) log_write(6, "<6>init: " x)

코드 3-6 | init 프로세스의 로그 메시지 출력 매크로

Page 58: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

40 l 인사이드 안드로이드

init 프로세스는 코드 3-6과 같이 __kmsg__ 장치를 통해 메시지를 출력하는 매크로를 정의해 두

고 있다. 해당 매크로를 통한 출력 메시지는 커널의 메시지를 보여주는 dmesg 유틸리티를 통해 확인

할 수 있다.

init 프로세스는 출력 장치를 생성한 이후 init.rc 파일을 파싱한다. init.rc 파일은 init 프로세스가

수행할 기능들을 기술해 놓은 파일이다. 일반적인 리눅스 시스템에서는 주로 루트 파일 시스템의 “/

etc/rc.d/” 디렉터리 밑에 부팅 시 실행할 실행 파일을 정의해 놓고, “/etc” 이하에 환경 변수를 설정하

는 스크립트를 둔다. 하지만 안드로이드는 init.rc, init.{hardware10 }.rc 두 파일만 사용하여 실행 파일

과 환경 변수를 정의한다. init.rc 파일은 안드로이드가 동작하는 데 있어 공통적으로 필요한 환경 설

정 및 프로세스에 대해 정의해 놓았고, init.{hardware}.rc는 안드로이드가 적용되는 플랫폼에 따라

특화된 프로세스나 환경 설정 등을 정의해 둔다.

parse_config_file("/init.rc");

코드 3-7 | main() -init.rc 파일 파싱

parse_config_file() 함수는 인자로 전달된 init.rc 파일을 읽어 파싱한 후 서비스 리스트와 액

션 리스트11 를 구성한다. 액션 리스트와 서비스 리스트는 init 프로세스에서 전역 구조체로 선언된

service_list와 action_list에 연결리스트 형태로 등록된다. 12 다음은 에뮬레이터 환경을 위해 QEMU

장치를 초기화하는 부분이다.

qemu_init();

코드 3-8 | main() - QEMU 장치 초기화

안드로이드 애플리케이션 개발자는 안드로이드가 탑재된 실제 장치가 없어도 QEMU 에뮬레이터

를 통해 개발중인 애플리케이션을 실행해볼 수 있다. QEMU는 PC를 위한 오픈소스 에뮬레이터로

서 특정 프로세서를 에뮬레이션하는 기능 이외에 가상의 네트워크, 비디오 하드웨어 장치 등을 제

공한다. 안드로이드는 에뮬레이터 환경을 제공하기 위해 QEMU 상에서 동작하는 ARM Core와

Gold¬sh라는 가상의 하드웨어 플랫폼을 구현했다. 따라서 에뮬레이터에서 실행되는 소프트웨어는

10  hardware는 안드로이드가 빌드될 플랫폼마다 달라진다. 예를 들어 삼성의 SMDK 개발 플랫폼은 init.smdk.rc와 같은 형태로 생성

된다.

11  init.rc 파일에서 service 키워드를 가진 내용들은 서비스 리스트라고 하며, 그외의 키워드들은 액션 리스트로 기술했다.

12 이와 관련된 부분은 3.3절 ‘init.rc 파일 분석 및 실행’에서 설명하고 있다.

Page 59: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 41

모두 ARM의 명령어 셋을 실행하며, LCD, 카메라, SD 카드 콘트롤러 등의 하드웨어 장치는 Gold¬sh

라는 가상의 하드웨어 플랫폼에 존재하게 된다.

다음은 코드 3-7에서 init.rc 파일을 분석했던 것처럼 init.{hardware}.rc 파일의 파싱을 진행한다.

snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);parse_config_file(tmp);

코드 3-9 | main() - init.{hardware}.rc 파일 파싱

init 프로세스는 코드 3-9의 parse_con¬g_¬le() 함수를 통해 init.{hardware}.rc 파일로부터 서비스

리스트와 액션 리스트를 생성한다. 이 리스트들은 init.rc 파일에서 이미 생성한 서비스 리스트와 액션

리스트에 각각 추가된다.

init 프로세스는 init.rc 파일의 ‘early-init, init, early-boot, boot’ 섹션에 포함된 명령어들을 순서대

로 실행한다. 코드 3-10을 통해 액션 리스트의 early-init 섹션에 해당하는 명령어들을 실행한다.

action_for_each_trigger("early-init", action_add_queue_tail);drain_action_queue();

코드 3-10 | main() - early-init 액션 실행

action_for_each_trigger() 함수는 첫 번째 인자에 해당하는 섹션의 명령어들을 실행 큐인 action_

add_queue_tail 에 저장한다. 그리고 drain_action_queue() 함수를 통해 실행 큐에 저장된 명령어들

을 하나씩 실행한다.

다음은 init 프로세스에 미리 정의돼 있는 디바이스 노드 파일을 생성하는 단계다.

device_fd = device_init();

코드 3-11 | main() - 정적 디바이스 노드 생성

init 프로세스는 코드 3-11의 device_init() 함수를 통해 정적 디바이스 노드를 생성한다. 노드 파일

생성에 대해서는 3.4절 ‘디바이스 노드 파일 생성’ 에서 자세히 다루겠다.

다음 단계에서 프로퍼티 서비스 초기화를 진행한다.

Page 60: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

42 l 인사이드 안드로이드

property_init();

코드 3-12 | main() - 프로퍼티 서비스 초기화

안드로이드는 시스템의 모든 프로세스에서 시스템의 설정 값을 공유하기 위해 프로퍼티라는 이름

의 저장소를 제공한다. 프로퍼티 영역은 init 프로세스가 코드 3-12의 property_init() 함수가 호출되

면서 공유 메모리 영역에 생성 및 초기화된다13. 이후, 동작중인 모든 프로세스에서 제공되는 API를

통해 프로퍼티 영역에 설정된 값을 조회할 수 있다. 그러나 값을 변경하는 것은 init 프로세스에서만

가능하다. 그렇기 때문에 값을 변경하고자 할 경우에는 init 프로세스에 변경 요청을 하고, init 프로세

스는 이 요청을 처리해 프로퍼티의 값을 변경한다.

다음 단계에서 안드로이드 부팅 로고를 LCD 화면에 출력한다.

#define INIT_IMAGE_FILE "/initlogo.rle"load_565rle_image(INIT_IMAGE_FILE);

코드 3-13 | main() - 부팅 로고 출력

코드 3-13의 load_565rle_image() 함수는 인자로 전달된 이미지 파일을 로드해서 LCD 화면에 출

력한다. 사용자가 부팅 로고를 변경하고 싶으면 위의 INIT_IMAGE_FILE을 수정하면 된다. 단, 해당

함수는 rle565 포맷14으로 된 이미지만 출력하므로 출력할 이미지의 포맷을 맞춰줘야 한다.

if (!strcmp(bootmode,"factory")) property_set("ro.factorytest", "1");else if (!strcmp(bootmode,"factory2")) property_set("ro.factorytest", "2");else property_set("ro.factorytest", "0");

property_set("ro.serialno", serialno[0] ? serialno : "");property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");property_set("ro.baseband", baseband[0] ? baseband : "unknown");property_set("ro.carrier", carrier[0] ? carrier : "unknown");property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");property_set("ro.hardware", hardware);snprintf(tmp, PROP_VALUE_MAX, "%d", revision);property_set("ro.revision", tmp);

코드 3-14 | main() - 프로퍼티 초기 설정

13 ASHMEM(Android Shared Memory)에 생성된다.

14 rle565 포맷 이미지는 안드로이드 빌드후 out/host/linux-x86/bin/rgb2565 -rle < img24bit.bmp > initlogo.rle와 같은 형태로 변환할수 있다.

Page 61: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 43

init 프로세스는 코드 3-14의 property_set() 함수를 이용하여 앞서 생성한 프로퍼티 영역에 시스템

운용에 필요한 몇몇 초기 값들을 설정한다. 부트로더로부터 커널로 전달된 부팅 옵션의 내용을 분석

해서 코드에서 보는 것처럼 ro.serailno, ro.bootmode 등과 같은 프로퍼티 값을 설정하게 된다. 이렇

게 설정된 프로퍼티들은 동작중인 여러 프로세스에서 property_get() API를 통해 조회할 수 있다.

다음은 액션 리스트의 init 섹션에 해당하는 명령어들을 실행한다.

/* execute all the boot actions to get us started */action_for_each_trigger("init", action_add_queue_tail);drain_action_queue();

코드 3-15 | main() - init 액션 실행

다음은 init 프로세스의 주요 기능 중 하나인 프로퍼티 서비스를 시작한다.

/* read any property files on system or data and * fire up the property service. This must happen * after the ro.foo properties are set above so * that /data/local.prop cannot interfere with them. */property_set_fd = start_property_service();

코드 3-16 | main() - 프로퍼티 서비스 시작

start_property_service() 함수에서는 앞에서 설정한 프로퍼티 외에 몇 개의 설정 파일을 읽어 프로

퍼티를 초기화한다. 루트 파일 시스템의 /data/property에는 운용 중에 프로세스에서 생성하거나 변

경된 내용이 저장돼 있다. 앞서 설명한 것처럼 프로퍼티 값의 변경은 init에서만 가능하다. 따라서 다

른 프로세스에서 값을 변경하는 것은 init 프로세스로 요청하여 처리가 되는데 이러한 요청을 받기 위

해 “/dev/socket/property_service” 소켓을 생성한다.

다음은 SIGCHLD 시그널 수신 시 자식 프로세스의 종료 처리 핸들러를 호출하기 위한 소켓을 생

성하는 부분이다.

/* create a signalling mechanism for the sigchld handler */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { signal_fd = s[0]; signal_recv_fd = s[1]; fcntl(s[0], F_SETFD, FD_CLOEXEC);

Page 62: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

44 l 인사이드 안드로이드

fcntl(s[0], F_SETFL, O_NONBLOCK); fcntl(s[1], F_SETFD, FD_CLOEXEC); fcntl(s[1], F_SETFL, O_NONBLOCK); }

코드 3-17 | main() - SIGCHLD 시그널 처리 시 사용할 UDS 소켓 생성

init 프로세스는 자식 프로세스의 종료 처리를 위한 핸들러를 따로 정의해 뒀는데, SIGCHLD 시

그널이 발생하자마자 해당 핸들러를 호출하기 위해 SIGCHLD 시그널 핸들러를 소켓을 통해 연결했

다. socketpair() 함수는 서로 연결된 소켓 쌍을 생성하는데, 이는 코드 3-17에서 s[2] 배열을 의미한

다. 코드 3-2에서 SIGCHLD 시그널의 핸들러에서 signal_fd를 1로 설정하는 부분을 볼 수 있는데, 이

때 소켓쌍(s)을 통해 signal_fd와 연결된 signal_recv_fd도 1로 설정된다. 이벤트 처리 핸들러에서는

signal_recv_fd의 값을 감시하는데, 1로 설정되면서 자식 프로세스 종료 처리 핸들러를 호출하게 된

다. 자세한 내용은 3.5절 ‘프로세스의 종료와 재시작’에서 살펴보겠다.

아래의 코드 3-18에서 액션 리스트의 early-boot와 boot, property 섹션에 해당하는 명령어를 실행

한다.

/* execute all the boot actions to get us started */action_for_each_trigger("early-boot", action_add_queue_tail);action_for_each_trigger("boot", action_add_queue_tail);drain_action_queue();

/* run all property triggers based on current state of the properties */queue_all_property_triggers();drain_action_queue();

코드 3-18 | main() - early-boot, boot, property 섹션의 액션 실행

init.rc의 boot 섹션은 서비스 리스트에 존재하는 프로세스의 리스트를 하나씩 실행하는 class_

start 명령어를 가지고 있다. class_start 명령어에 관한 것은 55쪽 3.3.4절 ‘액션 리스트 및 서비스 리스

트의 실행’에서 다루겠다.

다음은 이벤트 처리 루프에서 감시할 이벤트를 설정한다. POLL에 등록된 파일 디스크립터는

poll() 함수에서 이벤트를 기다리게 되며, 이벤트가 발생하면 poll() 함수를 빠져나와 이벤트를 처리하

게 된다.

Page 63: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 45

// device_fd = device_init(); - 코드 3-11// property_set_fd = start_property_service(); - 코드 3-16// signal_recv_fd = s[1]; - 코드 3-17

ufds[0].fd = device_fd; ❶ ufds[0].events = POLLIN; ufds[1].fd = property_set_fd; ❷ ufds[1].events = POLLIN; ufds[2].fd = signal_recv_fd; ❸ ufds[2].events = POLLIN; fd_count = 3;

코드 3-19 | main() - POLL 이벤트 등록

코드 3-19에서 init 프로세스가 감시할 파일 디스크립터를 등록한다. POLL에 등록할 파일 디스크

립터는 ❶ 디바이스 노드 생성 이벤트 처리를 위한 파일 디스크립터, ❷ 프로퍼티 서비스 요청 시 발생

하는 이벤트 처리를 위한 파일 디스크립터, 그리고 ❸ SIGCHLD 시그널 처리를 위한 파일 디스크립

터이다. 위 파일 디스크립터는 pollfd 구조체 변수인 ufds 배열에 각각 등록되고, 이벤트 처리 루프에

서 poll() 함수의 인자로 넘겨져 해당 파일 디스크립터의 이벤트를 감시한다. pollfd 구조체의 fd는 모

니터링할 파일 디스크립터 번호이고, events는 모니터링해야 할 이벤트다. POLL에 파일 디스크립터를

등록한 후에는 아래 이벤트 처리 루프에서 poll() 함수를 통해 이벤트를 감시하게 된다.

for(;;) { drain_action_queue(); ❶ restart_processes(); ❷ nr = poll(ufds, fd_count, timeout); ❸ if (ufds[2].revents == POLLIN) { ❹ /* we got a SIGCHLD - reap and restart as needed */ read(signal_recv_fd, tmp, sizeof(tmp)); while (!wait_for_one_process(0)) ; continue; }

if (ufds[0].revents == POLLIN) handle_device_fd(device_fd); ❺ if (ufds[1].revents == POLLIN) handle_property_set_fd(property_set_fd); ❻ }

코드 3-20 | main() - 이벤트 처리 루프

Page 64: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

46 l 인사이드 안드로이드

코드 3-20은 init 프로세스의 이벤트 처리 루프를 나타낸다.

❶ 이벤트의 발생을 확인하기 전에 액션 리스트에 들어 있는 명령들 가운데 아직 실행되지 않은 명

령들을 확인하고 실행한다. 처음 이벤트 처리 루프에서는 액션 리스트와 서비스 리스트에서 해

야 할 일이 없지만, 위에서 등록한 이벤트를 처리하고 난 뒤에는 액션 리스트와 서비스 리스트에

init 프로세스가 해야 할 일이 새로 등록된다. 따라서 ❶의 코드는 이벤트 처리 루프를 한번 수행

한 이후에 해당한다.

❷ 자식 프로세스가 종료됐을 때 자식 프로세스를 재시작하거나 종료하는 구문이다. restart_

processes() 함수에 대해서는 3.5.1절 ‘프로세스 재시작 코드 분석’에서 설명하겠다.

❸ poll() 함수를 통해 코드 3-19에서 등록한 파일 디스크립터에 발생한 이벤트를 기다린다. 이벤트

가 발생하면 파일 디스크립터가 등록된 pollfd 구조체의 변수인 udfs.revents에는 이벤트 정보가

담긴다. 따라서 poll() 함수가 반환된 이후에 ufds 배열의 revents에 어떠한 이벤트가 발생됐는지

조사한다.

❹ 자식 프로세스가 종료되어 SIGCHLD 시그널이 발생하면 udfs[2].revents에 POLLIN 이벤트가

등록된다. 이벤트 처리에 대해서는 3.5.1절 ‘프로세스 재시작 코드 분석’에서 설명하겠다.

❺ 안드로이드 동작 중 핫플러그 장치가 삽입됐을 때 디바이스 노드 파일을 생성하는 부분이다. 이

부분에 관해서는 3.4.2절 ‘동적 디바이스 감지’에서 자세히 설명한다.

❻ 프로퍼티 변경 요청에 대해 처리하는 부분이다. 프로퍼티에 관련된 내용은 3.6절에서 자세히 설

명하도록 한다.

3.3 init.rc파일분석및실행

이번 절에서는 안드로이드 부팅 시 시스템의 환경 설정과 실행할 프로세스를 기술해 놓은 파일인 init.

rc에 대해 설명하고, init 프로세스가 init.rc 파일을 통해 생성하는 액션 리스트와 서비스 리스트에 대

해서도 살펴본다. init.rc와 동일한 역할을 하는 init.{hardware}.rc 파일도 존재하지만 init 프로세스

는 init.{hardware}.rc도 init.rc와 동일하게 처리하고, 파일 내 액션 리스트와 서비스 리스트를 구분하

는 문법도 동일하므로 init.rc에 대해서만 분석하겠다.

init.rc 파일은 안드로이드를 빌드해야 생성되는 init 프로세스와는 달리 안드로이드 플랫폼의 소스

코드15 에서 살펴볼 수 있다. init.rc 파일의 형식은 대략 다음과 같다.

15 http://android.git.kernel.org/?p=platform/system/core.git;a=tree의 rootdir 디렉터리 이하에서 확인할 수 있다.

Page 65: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 47

on init

on boot

on property:ro.kernel.qemu=1on property:persist.service.adb.enable=1..

service console

service adbd

service servicemanager..

# setup the global environmentexport PATH /sbin:/system/sbin:/system/bin:/system/xbinexport LD_LIBRARY_PATH /system/lib..

# basic network initifup lohostname localhost..

액션리스트

서비스리스트

init.rc

그림 3-6 | ‘/init.rc’ 파일의 구조

그림 3-6과 같이 init.rc 파일 내용은 크게 ‘on’ 키워드로 시작하는 액션 리스트와 ‘service’ 키워드로

시작하는 서비스 리스트로 나뉜다. 액션 리스트는 시스템 환경 변수를 등록하거나 리눅스 명령어들

을 통해 부팅 시 필요한 디렉터리 생성이나 특정 파일에 대한 퍼미션을 지정한다. 그리고 서비스 리스

트는 부팅 시 실행하는 프로세스를 기술한다. 액션 리스트는 다음 절에서 살펴보며, 서비스 리스트는

3.3.2절 ‘서비스 리스트’에서 살펴보겠다.

3.3.1 액션 리스트

액션 리스트의 ‘on init’ 섹션에서는 환경변수를 등록(export)하고, 시스템 동작 시 필요한 파일 및 디렉

터리 생성하고 퍼미션을 조작한다. 그리고 시스템 동작과 관련된 디렉터리를 마운트한다

on init loglevel 3 # setup the global environment // 환경 변수 등록 export PATH /sbin:/system/sbin:/system/bin:/system/xbin export LD_LIBRARY_PATH /system/lib export ANDROID_BOOTLOGO 1 export ANDROID_ROOT /system # mount mtd partitions # Mount /system rw first to give the filesystem a chance to save a check point mount yaffs2 mtd@system /system // 루트 파일 시스템 마운트

mount yaffs2 mtd@system /system ro remount

# We chown/chmod /data again so because mount is run as root + defaults

Page 66: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

48 l 인사이드 안드로이드

mount yaffs2 mtd@userdata /data nosuid nodev chown system system /data chmod 0771 /data…

코드 3-21 | init.rc - on init 섹션

코드 3-21은 init.rc 파일 내 ‘on init’ 섹션의 내용이다. ‘on init’ 섹션의 환경변수 등록 부분에서는

루트 파일 시스템 내의 명령어들을 사용하기 위한 실행 경로를 등록하고, 프로그램 컴파일 시 필요한

라이브러리 경로를 등록한다.

on init 섹션의 루트 파일 시스템 마운트 부분에서는 /system과 /data 디렉터리를 마운트한다. 두 디

렉터리를 마운트하고 나면 안드로이드의 루트 파일 시스템이 완성된다. 안드로이드의 전체 루트 파일

시스템의 내용은 다음과 같다.

/

var tmp proc sys sbin etc root

bin appframe -work

libusr,fonts,mediaetc,soundslost+found

사용자 데이터shell 명령어 모음 기본 애플리케이션 Java 라이브러리 라이브러리

system

system,dalvik,cachedrm,logs,download

data,app 등

data

bionic

그림 3-7 | 안드로이드 루트 파일 시스템 구조

그림 3-7과 같이 루트 파일 시스템은 크게 셸 유틸리티, 라이브러리 그리고 기본 애플리케이션을 제

공하는 system 디렉터리와 안드로이드 동작 중에 개발자가 탑재한 사용자 애플리케이션이나 사진 데

이터 같은 사용자 데이터를 저장하는 data 디렉터리로 나뉜다. system/bin 디렉터리는 셸에서 동작하

는 유틸리티를 모아 두는 곳인데, 리눅스에서 자주 사용하는 busybox16 명령어 모음과 유사하다. 안드

로이드 플랫폼은 busybox 가 아닌 toolbox라는 이름의 명령어 모음을 제공한다.

안드로이드는 휴대폰을 타겟으로 제작된 OS이므로 기본적으로 휴대폰의 하드웨어 상태를 많이 따

16  안드로이드가 오픈소스이기는 하지만 상업용 기기에 탑재됐을 때 개발 업체의 S/W 지적 재산권을 보호하기 위해 소스 공개 원칙을

따르는 라이센스 정책들을 피했다. 따라서 안드로이드는 GPL 라이센스를 따르는 busybox를 사용하지 않고 toolbox를 사용했다.

Page 67: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 49

른다. 휴대폰 분야는 다양한 메모리 장치 중에서 공간 활용성이 좋은 저장 장치인 NAND 플래시를

선호한다. 따라서 NAND 플래시를 위한 파일 시스템인 ya·s2를 사용해 /system과 /data 디렉터리를

마운트한다. 만약 개발자가 마운트할 디렉터리나 파일 시스템을 변경하고 싶다면 코드 3-21에서 루트

파일 시스템을 마운트하는 부분을 수정해줘야 한다.

‘on boot’ 섹션에서는 애플리케이션 종료 조건 설정, 애플리케이션 구동에 필요한 디렉터리 및 파일

퍼미션 설정 등을 한다.

on boot# Define the oom_adj values for the classes of processes that can be // 애플리케이션 # killed by the kernel. These are used in ActivityManagerService. // 종료 조건 설정

setprop ro.FOREGROUND_APP_ADJ 0setprop ro.VISIBLE_APP_ADJ 1setprop ro.SECONDARY_SERVER_ADJ 2setprop ro.BACKUP_APP_ADJ 2setprop ro.HOME_APP_ADJ 4setprop ro.HIDDEN_APP_MIN_ADJ 7setprop ro.CONTENT_PROVIDER_ADJ 14setprop ro.EMPTY_APP_ADJ 15..class_start default // 서비스 실행

코드 3-22 | init.rc -‘on boot’ 섹션

코드 3-22는 init.rc 파일내 ‘on boot’ 섹션이다. ‘on boot’ 섹션의 애플리케이션 종료 조건 설정 부분

에서는 애플리케이션별 OOM(Out Of Memory) 조정 값(Adjustment Value:ADJ)을 지정한다. 안드로이

드 애플리케이션들은 구동 상태에 따라 그림 3-8과 같은 형태로 그룹이 나뉜다.

애플리케이션 그룹 ADJ 값 설명

FOREGROUND_APP 0 전면에 있는 프로그램

VISIBLE_APP 1 화면에는 보이지만 실행되지 않는 애플리케이션

SECONDARY_SERVER 2 서비스 데몬

HOME_APP 4 시작 화면에 등록되는 애플리케이션

HIDDEN_APP_MIN 7 최소화한 애플리케이션

CONTENT_PROVIDER 14 콘텐츠 프로바이더

EMPTY_APP 15

그림 3-8 | 애플리케이션 그룹 구분

Page 68: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

50 l 인사이드 안드로이드

OOM은 커널 상에서 애플리케이션에 할당되는 메모리를 모니터링하면서 메모리가 부족할 때 애플

리케이션을 종료시키는 역할을 한다. OOM 조정값은 메모리가 부족할 때 종료시킬 애플리케이션을

구분하는 데 사용되며, 값이 높을수록 종료 우선순위가 높아진다.

# adbd on at boot in emulatoron property:ro.kernel.qemu=1 start adbd

on property:persist.service.adb.enable=1 start adbd

on property:persist.service.adb.enable=0 stop adbd

코드 3-23 | init.rc 파일의 on property 설정

‘on property:<name>=<value>’ 섹션은 프로퍼티 값이 변경될 경우 실행되는 명령이 기술돼 있고,

기본적으로 제공되는 ‘init.rc’ 파일에는 ‘adbd’ 서비스의 시작, 종료에 대한 조건이 기술돼 있다. 프로

퍼티에 관한 설명은 72쪽 3.6절 ‘프로퍼티 서비스’에서 자세히 살펴보겠다.

3.3.2 서비스 리스트

init.rc 파일에서 ‘service’ 섹션은 init 프로세스가 실행시키는 프로세스를 기술한다. init 프로세스가

실행하는 자식 프로세스로는 부팅음을 출력하는 프로세스와 같은 일회성 프로그램이나 백그라운드

로 구동되면서 애플리케이션이나 시스템 운용에 관여하는 데몬 프로세스가 있다.

## Daemon processes to be run by init.##service console /system/bin/sh console

# adbd is controlled by the persist.service.adb.enable system propertyservice adbd /sbin/adbd disabled

service servicemanager /system/bin/servicemanager user system critical onrestart restart zygote onrestart restart media

Page 69: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 51

service vold /system/bin/vold socket vold stream 0660 root mount

코드 3-24 | init.rc - service 섹션

코드 3-24는 init.rc 파일 내 ‘service’ 섹션의 내용이다. ‘service’ 섹션에서 service 키워드 다음의

문자열은 실행할 프로세스의 이름을 나타내고 다음 문자열은 프로세스의 경로를 나타낸다. 그리고

다음 줄에는 프로세스의 실행에 관련된 옵션이 기술되는데, 여기에는 실행 권한, 실행 조건, 재시작

할 때 동작 등에 관한 옵션이 기술된다. ‘service’ 섹션의 프로세스는 모두 서비스 리스트에 등록되

며, init 프로세스가 실행되면서 서비스 리스트에 등록된 프로세스를 순차적으로 실행하게 된다. 서

비스 리스트에 기술된 프로세스 실행에 관해서는 55쪽 3.3.4절 ‘액션 리스트 및 서비스리스트의 실

행’ 을 살펴보자.

3.3.3 init.rc 파싱 코드 분석

3.2절 ‘init 프로세스 소스 코드 분석’의 코드 3-7에서 parse_con¬g_¬le() 함수를 통해 init.rc 파일을

분석한다고 했다. parse_con¬g_¬le() 함수는 인자로 전달되는 파일을 읽어 연속적인 문자열로 생성

하고, 각 열을 파싱한다. parse_con¬g_¬le() 함수는 코드 3-25와 같이 파일을 읽는 read_¬le() 함수

와 각 문자열을 파싱하는 parse_con¬g() 함수로 나뉜다.

int parse_config_file(const char *fn){ char *data; data = read_file(fn, 0); parse_config(fn, data);}

코드 3-25 | parse_config_file() 함수

read_¬le() 함수는 문자열을 저장할 메모리를 확보한 후 시작 주소를 반환하는 함수이므로 분석을

생략하고, read_¬le() 함수에 의해 생성된 각 문자열을 가지고 액션 리스트와 서비스 리스트를 생성

하는 parse_con¬g() 함수를 살펴보겠다. 코드 3-26의 parse_con¬g() 함수는 인자로 전달된 파일의

끝을 만날 때까지 파일의 각 라인을 파싱한다.

Page 70: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

52 l 인사이드 안드로이드

static void parse_config(const char *fn, char *s){ for (;;) { switch (next_token(&state)) { ❶ case T_NEWLINE: if (nargs) { int kw = lookup_keyword(args[0]); ❷ if (kw_is(kw, SECTION)) { ❸ parse_new_section(&state, kw, nargs, args); } }}

코드 3-26 | parse_config() 함수

❶ next_token() 함수는 인자로 넘겨받은 문자열을 라인 단위로 나눈 후 lookup_keyword() 함수

를 호출한다.

❷ lookup_keywork() 함수는 init.rc 파일의 각 라인에서 첫 단어에 해당하는 keyword_list 구

조체 배열에서 배열의 번호를 반환한다. keyword_list 구조체 배열은 코드 3-27에 정의된

KEYWORD 리스트에 의해 만들어지며, 각 리스트는 코드 3-28에 정의된 KEYWORD 매크로

를 사용해 생성된다.

// 키워드 , 그룹 , 인자개수 , 매핑 함수

KEYWORD(capability, OPTION, 0, 0)KEYWORD(class, OPTION, 0, 0)..KEYWORD(mkdir, COMMAND, 1, do_mkdir) KEYWORD(mount, COMMAND, 3, do_mount)KEYWORD(on, SECTION, 0, 0)KEYWORD(oneshot, OPTION, 0, 0)KEYWORD(onrestart, OPTION, 0, 0)KEYWORD(restart, COMMAND, 1, do_restart)KEYWORD(service, SECTION, 0, 0)..KEYWORD(chown, COMMAND, 2, do_chown)KEYWORD(chmod, COMMAND, 2, do_chmod)KEYWORD(loglevel, COMMAND, 1, do_loglevel)KEYWORD(device, COMMAND, 4, do_device)

코드 3-27 | KEYWORD 리스트

Page 71: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 53

KEYWORD 매크로는 parse.c와 keyword.h17에 정의돼 있는데, 각 파일에 정의된 KEYWORD

매크로는 서로 다른 역할을 한다. parse.c에 정의된 KEYWORD 매크로는 KEYWORD 리스트를

keyword_list 구조체 배열로 변환하는 역할을 하고, keyword.h에 정의된 KEYWORD 매크로는

KEYWORD 리스트들을 1번부터 순서대로 번호를 할당한다.

#define SECTION 0x01#define COMMAND 0x02#define OPTION 0x04

#define KEYWORD(symbol, flags, nargs, func) \ [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

struct { const char *name; int (*func)(int nargs, char **args); unsigned char nargs; unsigned char flags;} keyword_info[KEYWORD_COUNT] = { [ K_UNKNOWN ] = { "unknown", 0, 0, 0 }, #include "keywords.h" };

코드 3-28 | parse.c에 정의된 KEYWORD 매크로

코드 3-28에 정의된 KEYWORD 매크로는 코드 3-27의 주석의 그룹에 해당하는 필드별로 init.rc

에 정의된 키워드들을 그룹화한다. COMMAND 그룹은 init 프로세스가 실행하는 명령어들을 의미

하며, 해당 명령어를 처리하기 위해 실행 함수와 매핑된다. 예를 들어 mkdir 키워드는 디렉터리 생성

을 수행하는 함수인 do_mkdir과 매핑된다.

SECTION 그룹은 ‘on’ 키워드를 가지는 액션 리스트와 ‘service’ 키워드를 가지는 서비스 리스트를

구분한다. OPTION 그룹은 명령어를 실행할 때나 서비스 리스트의 프로세스를 실행할 때 실행 조건

을 부여한다.

KEYWORD 리스트들은 위 KEYWORD 매크로를 통해 [“리스트 번호”] = {“키워드”, “그룹”, ”인자

개수”, “매핑 함수”} 형태의 keyword_info 구조체 배열의 리스트로 변경된다. 그리고 “리스트 번호”는

코드 3-29에 정의된 KEYWORD 매크로에 의해 만들어진다. keyword.h에 정의된 KEYWORD 리스

트들을 “K_키워드” 심볼로 정의된 열거형 데이터로 변환되어 1부터 차례대로 번호가 부여된다.

17 http://android.git.kernel.org/?p=platform/system/core.git;a=tree의 init 디렉터리 아래에 존재한다.

Page 72: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

54 l 인사이드 안드로이드

#define __MAKE_KEYWORD_ENUM__#define KEYWORD(symbol, flags, nargs, func) K_##symbol,enum { K_UNKNOWN, // 0#endif KEYWORD(capability, OPTION, 0, 0) // 1 KEYWORD(class, OPTION, 0, 0) // 2

코드 3-29 | keyword.h에 정의된 KEYWORD 매크로

코드 3-30의 lookup_keyword() 함수는 인자로 전달된 키워드에 해당하는 KEYWORD 리스트의

번호를 반환한다.

int lookup_keyword(const char *s){ switch (*s++) { case ‘c’: if (!strcmp(s, "capability")) return K_capability; . . case ‘m’: if (!strcmp(s, "mkdir")) return K_mkdir; if (!strcmp(s, "mount")) return K_mount; break; case ‘o’: if (!strcmp(s, "on")) return K_on; if (!strcmp(s, "oneshot")) return K_oneshot; if (!strcmp(s, "onrestart")) return K_onrestart; break; case ‘s’: if (!strcmp(s, "service")) return K_service; . .}

코드 3-30 | lookup_keyword() 함수

만약 사용자가 init.rc에 새로운 명령어를 넣고 싶다면 위에서 설명한 KEYWORD 리스트에 새로운

명령어와 해당 명령어에 대한 함수를 새로 정의하면 된다.

❸ lookup_keyword() 함수가 반환하는 KEYWORD 리스트 번호를 가진 keyword_list 구조체 배

열을 찾는다. 그리고 배열 내 «ag가 SECTION인지 판단한다. SECTION «ag를 가지는 명령어는

“on” 키워드와 “service” 키워드 밖에 존재하지 않기 때문에 kw_is() 매크로를 통해 “on” 섹션에

Page 73: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 55

해당하는 액션 리스트와 “service” 섹션에 해당하는 서비스 리스트만 parse_new_section() 함

수를 수행한다.

parse_new_section() 함수는 kw_is() 매크로에 의해 구분된 명령어를 액션 리스트나 서비스 리스

트에 등록한다. 예를 들어 init.rc의 내용에서 “on” 섹션 이후의 명령어는 “service” 섹션을 만나기 전

까지 액션 리스트에 등록된다. 그리고 “service” 섹션 이하의 프로세스들은 서비스 리스트에 등록된

다. parse_new_section() 함수는 서비스 리스트와 액션 리스트를 각각 전역에 선언된 service_list와

action_list 변수에 저장한다. parse_new_section() 함수를 수행하고 나면 그림 3-9와 같은 형태로 액

션 리스트와 서비스 리스트가 완성된다.

sevice_list sevice

“console”

sevice

“adbd”

sevice

“servicemanager ”

action_list action

“init”

action

“boot”

“sysclktz 0” “loglevel 3”

. . .

. . .

“ifup lo” “ setrlimit 13 40 40” . . .

그림 3-9 | 서비스 리스트와 액션 리스트

다음 절에서는 서비스 리스트와 액션 리스트가 각각 어떻게 실행되는지 살펴보겠다.

3.3.4 액션 리스트 및 서비스 리스트의 실행

액션 리스트와 서비스 리스트는 코드 3-10에서 사용되는 drain_action_queue() 함수를 통해 실행

된다.

static void drain_action_queue(void){ struct listnode *node; struct command *cmd; struct action *act; int ret;

while ((act = action_remove_queue_head())) { ❶ INFO("processing action %p (%s)\n", act, act->name); list_for_each(node, &act->commands) {

Page 74: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

56 l 인사이드 안드로이드

cmd = node_to_item(node, struct command, clist); ❷ ret = cmd->func(cmd->nargs, cmd->args); ❸ } }}

코드 3-31 | drain_action_queue() 함수를 통한 액션 리스트 실행

코드 3-31은 drain_action_queue() 함수의 내용이다.

❶ action_remove_queue_head() 함수를 통해 전역으로 선언된 action_queue 연결 리스트의 헤

더를 얻어온다. action_queue는 실행할 명령어들의 액션리스트를 가지고 있다.

❷ action_queue로부터 가져온 액션 리스트를 command 구조체로 변환한다.

❸ command 구조체의 func 변수는 액션 리스트의 명령어에 연결된 실행 함수를 가리킨다. 이것은

3.3.3절 ‘init.rc 파싱 코드 분석’에서 설명했듯이 각 명령어에 맵핑된 함수에 해당한다. 예를 들어

init.rc에서 “mkdir /system”으로 선언된 라인은 do_mkdir() 함수를 호출하여 “/system” 디렉터

리를 생성하는 역할을 한다.

지금까지 액션 리스트 내의 명령어가 어떻게 실행되는지 살펴봤다. 그렇다면 서비스 리스트 내의

프로세스는 어떻게 실행될까? init 프로세스는 코드 3-22의 ‘on boot’ 섹션의 마지막 명령어인 class_

start 명령어를 통해 ‘service’ 섹션에 있는 모든 프로세스를 실행하게 된다. class_start 명령어에 해당

하는 실행 함수는 do_class_start()18 함수다.

static void service_start_if_not_disabled(struct service *svc){ if (!(svc->flags & SVC_DISABLED)) { service_start(svc); }}

int do_class_start(int nargs, char **args){ /* Starting a class does not start services * which are explicitly disabled. They must * be started individually. */ service_for_each_class(args[1], service_start_if_not_disabled); return 0;}

코드 3-32 | do_start_class() 함수

18  http://android.git.kernel.org/?p=platform/system/core.git;a=tree의 init 디렉터리 아래에 있는 builtins.c에 존재한다.

Page 75: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 57

코드 3-32는 class_start 명령어의 실행 함수인 do_start_class() 함수이다. do_start_class() 함수

는 service_for_each_class() 함수를 통해 전역적으로 선언된 service_list 구조체, 즉 서비스 리스

트 내에 선언된 프로세스 목록을 service_start() 함수를 통해 실행시킨다. service_start() 함수는

execve() 시스템 콜을 통해 서비스 리스트에 있는 프로세스를 실행시킨다. 이 과정을 통해 init 프로세

스는 init.rc에 정의된 모든 프로세스를 실행하게 된다.

init.rc에 저장된 내용은 AIL(Android Init Language)라는 문법을 통해 작성되었다. 문법에 대한

정의는 init 프로세스 소스 코드 디렉터리에 들어 있는 readme.txt 파일을 통해 살펴볼 수 있다. 아래

팁에 해당 내용을 간략히 정리했으니 참고한다.

Android Init Language

안드로이드 플랫폼에서 init 프로세스가 수행할 환경 설정과 실행할 프로세스에 대해 ‘init.rc’ 파일이 이용된다.

이 파일을 기술하기 위한 작성 규칙이 Android Init Language이며, 여기서 간략하게나마 알아보겠다.

AIL에서는 모든 문장이 네 가지 종류로 나뉜다.

▒ Action

▒ Commands

▒ Services

▒ Options

문장의 구성 규칙은 다음과 같다.

▒ 문장을 구성하는 빈칸은 토큰으로 구분되며, 토큰에 빈칸 자체를 포함할 경우 C 스타일의 백 슬래쉬를 사용

한다. 큰따옴표를 사용하면 여러 개의 토큰을 하나의 토큰으로 처리할 수 있다. 문장의 마지막 줄에 나타나

는 백 슬래시 기호는 다음 줄과 이어짐을 표시한다.

▒ ‘#’으로 시작되는 문장은 주석으로 처리된다.

▒ ‘Action’과 ‘Service’ 키워드는 새로운 섹션을 시작하는 것을 의미한다. ‘Command’와 ‘Option’은 가장 마지

막에 정의된 섹션에 종속된다.

▒ ‘Action’과 ‘Service’는 반드시 유일한 이름이어야 한다. 만약 이름이 같은 ‘Action’이나 ‘Service’가 있으면

오류로 처리되며, 무시된다.

Action

각 action은 기능을 수행하기 위한 이벤트, 즉 trigger가 있다. ‘on’ 키워드로 시작되는 이벤트가 발생하면 다음

에 기술된 명령이 순차적으로 실행된다. 형식은 다음과 같다.

on <trigger><command><command><command>

Page 76: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

58 l 인사이드 안드로이드

ServiceService 섹션에서는 init 프로세스가 실행하거나 재실행할 프로세스를 기술한다. 형식은 다음과 같다.

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

Option

Service를 실행할 때 실행 옵션을 줄 수 있으며, 다음의 옵션을 사용할 수 있다.

disabled init이 실행하는 모든 프로세스는 클래스로 불리는 실행 그룹에 포함된다. 실행된

프로세스가 속한 클래스가 실행되더라도 disabled 옵션이 설정된 프로세스는 실행

되지 않으며, 클래스와는 별도로 명시적으로 실행해줘야 한다.

socket <type>

<name> <perm> [

<user> [ <group> ] ]

‘/dev/socket/<name>’으로 Unix domain socket을 생성하고, 생성한 소켓 디스

크립터를 실행할 프로세스로 전달한다.

user <username> 실행될 프로세스의 사용자를 변경한다. 설정하지 않을 경우 사용자는 ‘root’로 설정

된다.

group

<groupname>

[ <groupname> ]*

실행될 프로세스의 그룹을 설정한다. 설정하지 않을 경우 그룹은 ‘root’로 설정된

다.

oneshot 실행된 프로세스가 종료된 후에 다시 실행되지 않는다.

class <name> 실행될 프로세스가 속한 클래스를 명시한다. 클래스를 시작/종료하면 클래스에 속

한 모든 프로세스를 시작/종료하게 된다. 만약 명시하지 않은 경우 “default” 클래

스에 속하게 된다.

onrestart 프로세스가 재시작될 때 실행될 명령을 기술한다.

Trigger

‘on’ 키워드와 함께 사용되며 action이 수행되기 위한 이벤트는 다음처럼 명시되거나, 문법을 사용해 기술할 수

있다.

boot init이 시작될 때 발생한다.

<name>=<value> 프로퍼티가 변경될 때 발생한다.

device-added-<path>

device-removed-<path>

장치가 추가되거나 제거될 때 발생한다.

service-exited-<name> 특정 프로세스가 종료될 때 발생한다.

Page 77: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 59

Command

다음과 같은 명령들을 기술하여 수행할 수 있다.

exec <path> [ <argument> ]* 명령을 실행한다. 실행이 종료될 때까지 init이 대기 상태가 되므

로 주의해야 한다.

export <name> <value> 환경 변수를 설정한다. 명령이 실행된 이후 실행되는 모든 프로세

스에서 이 환경 변수를 참조할 수 있다.

ifup <interface> 네트워크 인터페이스를 활성화한다.

import <filename> 다른 설정 파일을 추가로 읽어들인다.

hostname <name> 호스트 네임을 설정한다.

class_start <serviceclass> 클래스에 속한 모든 프로세스들을 실행한다.

domainname <name> 도메인 네임을 설정한다.

insmod <path> 디바이스 모듈을 로딩한다.

loglevel <level> 로그 레벨을 설정한다.

mkdir <path> 디렉터리를 생성한다.

mount <type> <device> <dir> [

<mountoption> ]*

파일 시스템을 디렉터리에 마운트한다.

setprop <name> <value> 프로퍼티를 설정한다.

setrlimit <resource> <cur> <max> 프로세스에서 사용할 자원에 대한 제한을 설정한다. 자원에는 사

용자가 생성할 수 있는 파일의 크기, 열 수 있는 파일의 수 등이 있

다. 자세한 내용은 setlimit의 manpage를 참고한다.

start <service> 프로세스를 실행한다. 이미 실행되고 있다면 무시된다.

stop <service> 동작중인 프로세스를 중지시킨다.

symlink <target> <path> 심볼릭 링크를 생성한다.

write <path> <string> [ <string> ]* 파일을 열어 문자열을 기록한다.

좀더 자세한 설명은 http://pdk.android.com/online-pdk/guide/bring_up.html에서 확인할 수 있다.

3.4 디바이스노드파일생성

리눅스와 마찬가지로 안드로이드에서도 애플리케이션은 디바이스 드라이버를 통해 하드웨어에 접근

한다. 애플리케이션에서는 디바이스 드라이버에 접근하기 위해 디바이스 드라이버의 논리적 파일인

디바이스 노드 파일을 사용한다. 리눅스에서는 디바이스 노드 파일을 생성할 수 있게 mknod 유틸리

Page 78: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

60 l 인사이드 안드로이드

티를 제공하지만, 안드로이드에서는 보안 문제로 인해 mknod와 같은 디바이스 생성 유틸리티를 제공

하지 않는다. 따라서 안드로이드에서 디바이스 노드 파일을 생성하려면 안드로이드에서 제공하는 디

바이스 노드 파일 생성 방법을 따라야 한다. 본 절에서는 안드로이드가 어떻게 디바이스 노드를 생성

하는지 살펴보겠다.

3.4.1 정적 디바이스 노드 생성

일반적으로 리눅스에서는 동작 중에 필요한 디바이스 노드 파일을 “/dev” 디렉터리에 미리 정의해둔

다. 따라서 애플리케이션은 별도의 절차를 거치지 않고도 미리 정의된 디바이스 노드 파일을 통해 디

바이스 드라이버에 접근한다. 하지만 안드로이드의 루트 파일 시스템 이미지에는 “/dev” 디렉터리가

존재하지 않는다. 따라서 시스템 운용 중에 누군가가 디바이스 노드 파일을 생성해줘야 하는데, 이러

한 역할을 init 프로세스가 담당한다.

init 프로세스는 두 가지 방법으로 디바이스 노드 파일을 생성한다. 첫째는 미리 정의된 디바이스

정보를 바탕으로 init 프로세스가 실행될 때 일괄적으로 디바이스 노드 파일을 생성하는 방법이다. 둘

째는 시스템 동작중 USB 포트에 장치가 삽입될 때 이에 대한 이벤트 처리로 init 프로세스가 해당 장

치의 디바이스 노드 파일을 동적으로 생성하는 방법이다.

첫 번째 방법은 이미 정의된 디바이스를 연결하는 방식이라 하여 콜드플러그(Cold Plug)라고 하며,

두 번째 방법은 동작 중에 연결한다고 하여 핫플러그(Hot Plug)라고 한다. 핫플러그에 대해서는 다음

절에서 살펴보기로 하고, 이번 절에서는 콜드플러그에 대해서 좀더 살펴보자.

리눅스 커널 2.6 미만에서는 디바이스 노드 파일을 사용자가 직접 만들어야 했다. 이때 노드 파일에

필요한 메이저 번호와 마이너 번호를 겹치지 않게 만든 다음 mknod 유틸리티를 통해 생성했다. 하지

만 사용자가 일일이 메이저 번호와 마이너 번호를 알아야 했고 번호 간의 충돌을 주의해야 하는 등의

불편함 때문에 커널 2.6x부터는 udev(userspace device)라는 유틸리티가 등장했다. udev는 데몬 프로

세스로 동작하면서 디바이스 드라이버가 로딩될 때 메이저 번호와 마이너 번호, 디바이스 타입을 파

악해서 “/dev” 디렉터리에 자동으로 디바이스 노드 파일을 생성하는 역할을 한다.

Page 79: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 61

드라이버초기화 함수

udev데몬 프로세스

“/dev” “/sys”

(1) 드라이버의 정보 등록

(2) uevent 발생

(3) uevent의 메시지와 “/sys” 에 등록된 디바이스 정보를 비교

(4) 디바이스 노드 파일 생성

그림 3-10 | udev 데몬을 통한 디바이스 노드 파일 생성

그림 3-10은 디바이스 드라이버가 로딩될 때 udev를 통해 디바이스 노드 파일을 생성하는 과정이다.

시스템 동작 중에 장치가 삽입되면 커널은 해당 장치에 대한 드라이버를 로딩한다. 드라이버는 드라이

버의 시작 함수인 probe() 함수19 를 호출하고, 함수 내에서 “/sys” 파일 시스템에 드라이버의 메이저 번

호와 마이너 번호, 디바이스 타입을 저장한다. 그러고 나서 udev 프로세스에 uevent를 발생시킨다.

uevent란?

커널은 디바이스 추가 및 제거 시 해당 디바이스의 정보를 사용자 공간으로 전달하기 위해 uevent를 사용한

다. uevent는 커널에서 사용자 공간의 프로세스로 메시지를 전달하기 위한 신호 체계다. uevent는 디바이스

이름, 종류, 주 번호, 부 번호, 디바이스 노드 파일이 생성될 경로 정보를 담고 있으며, 이를 udev 데몬에 전달

한다. udev는 uevent를 감지하고 uevent가 실어준 메시지(디바이스 정보들)를 파악하여 디바이스 노드 파

일을 생성한다.

백그라운드로 실행중인 udev 데몬은 드라이버의 uevent 메시지를 분석하여 /sys 디렉토리에 등록

된 디바이스 정보를 보고 “/dev” 디렉터리의 적절한 위치에 디바이스 노드 파일을 생성한다. udev 데

몬은 커널의 부팅 과정 이후 사용자 공간에서 동작하는 프로세스이므로 커널 부팅 과정에서 발생하

는 디바이스 드라이버의 uevent를 처리하지 못하는 문제가 있다. 따라서 커널 공간의 디바이스 드라

이버는 정상적으로 동작하지만 해당 디바이스 드라이버에 접근하기 위한 디바이스 노드 파일이 생성

되지 않기 때문에 애플리케이션은 해당 디바이스를 사용할 수 없는 문제가 생긴다.

리눅스에서는 udev 데몬 실행 이전에 로딩된 디바이스 드라이버에 대해 콜드플러그를 제공하여 디

바이스 노드 파일이 생성되지 않는 문제를 해결한다. 콜드플러그는 장치가 삽입됐을 때 바로 처리하

19 디바이스 드라이버에 정의된 probe() 함수는 커널에 의해서 자동으로 호출되며 디바이스를 초기화하는 역할을 한다.

Page 80: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

62 l 인사이드 안드로이드

는 핫플러그와 달리 이미 삽입된 장치에 대한 처리를 담당하는 메커니즘이다. 콜드플러그는 커널 부

팅 후에 udev 데몬이 실행되면서 “/sys” 디렉토리에서 미리 등록된 디바이스 정보를 읽어들인 후 각

디바이스에 대해 uevent를 다시 발생시켜 디바이스 노드 파일을 생성하는 방식이다. 안드로이드도 이

와 동일한 방식으로 디바이스 노드 파일을 생성하는데, udev 데몬의 역할을 init 프로세스가 수행한

다는 차이가 있다.

콜드플러그 방식으로 디바이스 노드를 생성하는 예로 7장 ‘안드로이드 바인더 IPC’에서 설명할 바

인더 드라이버를 살펴보자. 바인더 드라이버는 실제 하드웨어가 존재하지 않는 가상의 장치이며, 프로

세스 간의 RPC(Remote Procedure Call)를 제공하는 데 쓰인다. 바인더를 이용하고자 하는 애플리케이

션은 “/dev/binder”라는 디바이스 노드를 사용해서 바인더 드라이버에 접근해야 한다.

커널 부팅 시 바인더 드라이버는 초기화 함수에서 misc_register() 함수를 호출한다. misc_

register() 함수는 그림 3-11의 (1)에서 디바이스 노드 파일을 생성하는 데 필요한 정보를 “/sys” 디렉토

리 밑에 저장한다.

X

바인더드라이버초기화함수

init프로세스

“/sys”

(1) 드라이버의 정보(“주번호, 부번호, 디바이스 타입”) 등록

(2) uevent 발생 실패

init 프로세스 실행 전

그림 3-11 | 바인더 드라이버의 정보 등록

바인더 드라이버는 사용자 공간의 init 프로세스를 통해 디바이스 노드 파일을 생성해야 하지만 아

직 커널 부팅 단계이므로 init 프로세스로 uevent를 발생시킬 수 없다. 따라서 “/sys” 디렉토리에만 드

라이버 정보를 등록하고 디바이스 노드 파일을 생성하는 절차를 종료한다.

커널 부팅이 완료되고 init 프로세스가 실행되면 위의 바인더 드라이버의 경우처럼 디바이스 노드

파일을 생성하지 못한 드라이버에 대해 콜드플러그 처리를 한다. init 프로세스는 콜드플러그로 처리

될 드라이버들을 이미 알고 있으며, 각 드라이버의 디바이스 노드 파일을 미리 정의해 두고 있다. 안드

로이드 코드의 /system/core/init/devices.c 파일에는 init 프로세스가 생성하는 노드 파일 목록이 아

래 코드 3-33과 같이 나열돼 있다.

Page 81: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 63

static struct perms_ devperms[] = { { "/dev/null", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/zero", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/full", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/ptmx", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/tty", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/random", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/urandom", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/ashmem", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/binder", 0666, AID_ROOT, AID_ROOT, 0 },{ "/dev/log/", 0662, AID_ROOT, AID_LOG, 1 },/* these should not be world writable */ { "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 },{ "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 },

코드 3-33 | 콜드플러그 디바이스의 노드 파일 리스트

init 프로세스는 콜드플러그 처리 시에 devperms 구조체를 참고하여 “/dev” 디렉터리에 디바이스

노드 파일들을 생성한다. 코드 3-33의 devperms 구조체는 콜드플러그될 드라이버의 디바이스 노드

파일 이름, 접근권한, 사용자 ID, 그룹 ID를 나타낸다. 만약 사용자가 정의한 새로운 장치에 대해 디바

이스 노드 파일을 생성하고 싶다면 devperms 구조체에 해당 드라이버의 정보를 추가하면 된다.

이제 init 프로세스의 콜드플러그 처리 절차를 살펴보자. init 프로세스는 코드 3-11에서 device_

init() 함수를 호출한다.

int device_init(void){ fd = open_uevent_socket(); t0 = get_usecs(); coldboot(fd, "/sys/class"); coldboot(fd, "/sys/block"); coldboot(fd, "/sys/devices"); t1 = get_usecs(); log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));}

코드 3-34 | init.c - device_init() 함수

코드 3-34는 device_init() 함수를 보여준다. device_init() 함수는 먼저 uevent를 수신하기 위

한 소켓을 생성한다. 그리고 coldboot() 함수는 다시 do_coldboot() 함수를 호출하여 커널 부팅 시

에 “/sys” 디렉토리에 정보를 등록한 드라이버에 대해 콜드플러그 처리를 한다. 코드 3-35의 do_

coldboot() 함수를 살펴보자.

Page 82: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

64 l 인사이드 안드로이드

static void do_coldboot(int event_fd, DIR *d){ fd = openat(dfd, "uevent", O_WRONLY); if(fd >= 0) { write(fd, "add\n", 4); close(fd); handle_device_fd(event_fd); }}

코드 3-35 | devices.c - do_coldboot() 함수를 통해 uevent를 발생시킴

do_coldboot() 함수는 인자로 디렉터리 경로를 받아 해당 경로를 이용해 저장된 uevent 파일을

찾은 후, 해당 파일에 “add” 메시지를 써넣어 강제로 uevent를 발생시킨다. 그러고 나서 handler_

device_fd() 함수에서 해당 uevent를 수신하고 uevent에 실린 메시지를 파악한다.

struct uevent { const char *action; const char *path; const char *subsystem; const char *firmware; int major; int minor;}void handle_device_fd(int fd){ while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) { struct uevent uevent; parse_event(msg, &uevent); handle_device_event(&uevent); }}

코드 3-36 | uevent 처리

코드 3-36은 uevent를 처리하는 과정을 보여준다. handle_device_fd() 함수는 uevent가 수신되면

parse_event() 함수를 호출하여 uevent의 메시지를 uevent 구조체에 할당한다. 예를 들어 바인더 드

라이버의 디바이스 노드 생성을 위해 전달되는 메시지는 uevent 구조체의 각 멤버 변수에 할당되어

다음과 같은 값을 가진다.

Page 83: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 65

struct uevent { const char *action; // "add" const char *path; // "device/virtual/misc/binder" const char *subsystem; // "misc" const char *firmware; // int major; // 10 int minor; // 62}

코드 3-37 | 바인더 드라이버 노드 추가를 위한 uevnet 구조체 내용

코드 3-37에서 바인더 드라이버의 uevent 메시지에서 init 프로세스가 써넣은 “add” 액션과 커널 부

팅 시 바인더 드라이버가 “/sys” 디렉토리에 써놓은 디바이스 정보를 확인할 수 있다.

uevent 구조체가 완성되면 handle_device_event() 함수를 호출하여 실제 노드 파일을 생성하는

과정을 거친다.

static void handle_device_event(struct uevent *uevent){ . . if(!strncmp(uevent->subsystem, "block", 5)) { block = 1; base = "/dev/block/"; mkdir(base, 0755); } . . else base = "/dev/"; if(!strcmp(uevent->action, "add")) { make_device(devpath, block, uevent->major, uevent->minor); }}

코드 3-38 | handle_device_event() 함수를 통한 /dev 아래의 하위 디렉터리 생성

코드 3-38의 handler_device_event() 함수는 먼저 uevent 구조체의 subsystem을 파악한 후 “/

dev” 디렉터리 아래에 하위 디렉터리를 생성한다. subsystem은 하드웨어 장치의 사용 용도에 따른 그

룹을 나타낸다. 예를 들어 저장 장치일 경우 subsystem은 block이 되고 “/dev/block” 디렉토리를 생성

한다. 그리고 그래픽 관련 장치일 경우 “/dev/graphic”, 오디오 장치일 경우 “/dev/adsp” 디렉토리를 생

성한다. 앞서 소개한 바인더 드라이버의 경우 subsystem이 misc이지만 handle_device_event() 함수

Page 84: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

66 l 인사이드 안드로이드

에서는 misc에 대한 하위 디렉터리를 따로 생성하지 않는다. 하위 디렉터리를 모두 생성하면 make_

device() 함수를 호출해서 디바이스 노드 파일을 생성한다.

static void make_device(const char *path, int block, int major, int minor){ mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); dev = (major << 8) | minor; mknod(path, mode, dev); chown(path, uid, gid);}

코드 3-39 | device.c – make_device() 함수를 통한 디바이스 노드 생성

코드 3-39의 make_device() 함수는 코드 3-33에서 선언된 디바이스 노드 파일 리스트에서 사용

자 ID와 그룹 ID 정보를 얻어온다. 그러고 나서 mknod() 함수를 호출해서 디바이스 노드 파일을 생

성한다.

3.4.2 동적 디바이스 감지

init 프로세스는 시스템 동작 중에 추가되는 장치의 디바이스 노드 파일 생성을 위해 핫플러그 처리

를 지원한다. 핫플러그는 init 프로세스의 이벤트 처리 루프에서 처리된다.

int main(int argc, char **argv){ for(;;) { nr = poll(ufds, fd_count, timeout); . . if (ufds[0].revents == POLLIN) handle_device_fd(device_fd); } ...}

코드 3-40 | init.c - main() - 디바이스 이벤트 처리

코드 3-40은 init 프로세스의 이벤트 처리 루프 중 핫플러그를 통한 디바이스 노드 파일을 생성하

는 부분을 보여준다. init 프로세스는 이벤트 처리 루프에서 poll() 함수를 통해 드라이버로부터 발생

하는 uevent를 감지하고 handle_device_fd() 함수를 호출한다. 그러고 나서 코드 3-36에서 살펴본

handle_device_fd() 함수를 통해 디바이스 노드 파일을 생성한다.

Page 85: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 67

3.5 프로세스종료와재시작

init 프로세스는 init.rc 파일로부터 파싱한 서비스 리스트를 통해 자식 프로세스를 순차적으로 실행

한다. 여기서 잠깐 init이 실행하는 주요 프로세스를 살펴보자.

�sh : 안드로이드가 탑재된 기기에 대해 터미널, 시리얼 혹은 adbd 접속 시 콘솔 입출력을 제공하는

셸 프로그램이다.

�adbd : Android Debug Bridge 데몬을 의미하며, QEMU 에뮬레이터나 실제 기기의 상태를 관리

하는 데 쓰는 툴이다. 이 툴은 클라이언트-서버 프로그램의 일종으로 타겟(에뮬레이터 혹은 기기)

상에서 동작하는 서버이며, PC는 서버에 접속하는 클라이언트가 된다. 예를 들어 애플리케이션 개

발 시 이클립스의 DDMS(Dalvik Debug Monitor Service)를 이용해 타겟을 디버깅하는데, 이때

DDMS는 adb 데몬에 대한 클라이언트가 된다.

�servicemanager : 7장 ‘안드로이드 바인더 IPC’에서 소개할 컨텍스트 매니저이다. 이 프로그램

은 안드로이드 시스템 서비스의 목록을 관리한다.

�vold : Volume 데몬을 의미한다. USB 스토리지나 SD 카드 장치를 마운트하고 관리한다.

�playmp3 : 안드로이드 부팅 시 부팅 사운드를 출력한다.

위에서 설명한 프로세스 외에도 다양한 프로세스가 init 프로세스에 의해 실행된다. init 프로세스

가 실행하는 프로세스 가운데에는 종료되면 시스템 동작에 영향을 미치는 것들이 존재한다. 예를 들

어 servicemanager의 경우 애플리케이션이 시스템 서비스를 사용하는 데 반드시 필요한 프로세스인

데, 이 프로세스가 종료되면 프로세스 간 통신이나 그래픽 출력, 오디오 출력과 같은 기능을 사용할

수 없게 된다. 따라서 init 프로세스가 실행하는 프로세스는 일부를 제외하고 대부분 종료되더라도

init 프로세스에 의해 재시작된다.

init 프로세스

자식프로세스생성 자식프로세스종료

init 프로세스

SIGCHLD 시그널발생

프로세스의옵션체크

자식프로세스재시작

서비스리스트내프로세스실행

on shot?e 재시작하지않음

y

nservice_start () service_start ()

그림 3-12 | init 프로세스의 SIGCHLD 시그널 처리

Page 86: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

68 l 인사이드 안드로이드

3.3.2절 ‘서비스 리스트’에서 살펴보았듯이 init 프로세스는 서비스 리스트의 프로세스를 실행하여

자식 프로세스를 생성한다. 그림 3-12에서 볼 수 있듯이 자식 프로세스가 종료되면 자식 프로세스는

SIGCHLD 시그널을 부모 프로세스인 init 프로세스로 전달한다. 그러고 나면 init 프로세스는 시그널

이 발생한 프로세스를 다시 실행하는 과정을 거치는데, 해당 프로세스의 옵션에 oneshot이 정의돼 있

다면 재시작하지 않는다.

다음 절에서는 init 프로세스가 자식 프로세스를 어떻게 재시작시키는지 코드를 통해 살펴보겠다.

3.5.1 프로세스 재시작 코드 분석

init 프로세스는 코드 3-20의 이벤트 처리 루프에서 대기하다가 자식 프로세스가 종료되면

SIGCHLD 시그널에 대한 핸들러를 수행한다. 시그널 핸들러는 코드 3-2에서 등록한 sigchld_

handler() 함수다.

static void sigchld_handler(int s){ write(signal_fd, &s, 1);}

코드 3-41 | sigchld_handler() 함수

sigchld_handler() 함수는 SIGCHLD 시그널에 의해 호출되므로 인자로 SIGCHLD 시그널 번호를

받는다. 그리고 해당 번호를 코드 3-17에서 생성한 소켓 디스크립터에 기록한다.

signal_fd signal_recv_fd

socketpair()sigchld_handler()

자식 프로세스 종료

SIGCHLD 시그널 발생

SIGCHLD 번호 쓰기

struct pollfd

device_fd property_set_fd signal_recv_fdwait_for_one_process(0)

poll() handle_device_fd()

handle_property_set_fd()

소켓을 통한 데이터 전달

이벤트 발생

코드 3-2

코드 3-19 코드 3-20

코드 3-17

그림 3-13 | SIGCHLD 시그널이 발생된 후 재시작 처리 함수를 호출하는 과정

Page 87: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 69

그림 3-13은 SIGCHLD 시그널이 발생하고 나서부터 자식 프로세스 종료 이벤트 처리 함수인

wait_for_one_process() 함수가 호출되기까지의 과정이다. 시그널 번호가 기록된 signal_fd는 소켓

쌍으로 생성됐기 때문에 수신측 소켓 디스크립터인 signal_recv_fd로 시그널 번호가 전달된다. 시그

널 번호를 받은 signal_recv_fd는 코드 3-19에서 이미 POLL로 등록돼 있기 때문에 wait_for_one_

process() 함수를 실행하게 된다. 아래 코드 3-42는 이벤트 처리 루프에서 wait_for_one_process()

함수가 호출되는 부분이다.

for(;;) { nr = poll(ufds, fd_count, timeout); ❶ if (nr <= 0) continue;

if (ufds[2].revents == POLLIN) { /* we got a SIGCHLD - reap and restart as needed */ read(signal_recv_fd, tmp, sizeof(tmp)); while (!wait_for_one_process(0)); ❷ continue; } ...}

코드 3-42 | 이벤트 처리 루프에서 SIGCHLD 시그널을 처리하는 부분

❶ poll() 함수는 SIGCHLD 시그널 발생 시 이벤트 감시 상태에서 빠져 나와 이후의 코드를 실행

한다.

❷ signal_recv_fd는 ufds[2]에 등록돼 있고 데이터 입력에 의해 이벤트가 발생했기 때문에 wait_

for_one_process() 함수를 수행한다. wait_for_one_process() 함수는 SIGCHLD 시그널을 발

생시킨 프로세스가 가진 서비스 리스트에서 옵션을 체크하고, 옵션이 oneshot(SVC_ONE_

SHOT)이 아닌 경우 재시작 옵션(SVC_RESTARTING)을 추가한다. oneshot 옵션은 init.rc 의

service 섹션에 정의되어 있으며, oneshot 옵션을 가진 프로세스는 재시작되지 않는다.

코드 3-43은 wait_for_one_process() 함수를 보여준다.

static int wait_for_one_process(int block){ … while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 ❶ && errno == EINTR ); svc = service_find_by_pid(pid); ❷

Page 88: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

70 l 인사이드 안드로이드

if (!(svc->flags & SVC_ONESHOT)) { ❸ kill(-pid, SIGKILL); } /* remove any sockets we may have created */ for (si = svc->sockets; si; si = si->next) { ❹ unlink(tmp); }

svc->pid = 0; ❺ svc->flags &= (~SVC_RUNNING);

if (svc->flags & SVC_ONESHOT) { ❻ svc->flags |= SVC_DISABLED; } if (svc->flags & SVC_DISABLED) return 0; } list_for_each(node, &svc->onrestart.commands) { ❼ cmd = node_to_item(node, struct command, clist); cmd->func(cmd->nargs, cmd->args); }

svc->flags |= SVC_RESTARTING; ❽}

코드 3-43 | init.c - wait_for_one_process() - waitpid(-1) 실행

❶ waitpid() 함수는 시그널을 발생시킨 자식 프로세스가 종료되면 해당 프로세스에 할당된 자원

을 회수한다. 첫 번째 인자인 ‘-1’은 특정 pid가 아닌 모든 자식 프로세스에 대해 SIGCHLD가 발

생한 것인지를 확인한다. 두 번째 인자인 status는 자식 프로세스의 상태를 반환한다. 마지막인

자는 waitpid() 함수를 블럭으로 처리할 것인지 결정한다. waitpid() 함수는 pid 값을 반환하며,

반환된 값은 SIGCHLD 시그널이 발생한 프로세스의 pid를 의미한다.

❷ service_¬nd_by_pid() 함수를 통해 서비스 리스트에서 종료된 프로세스에 해당하는 서비스 항

목을 가져온다.

❸ 가져온 서비스 항목의 옵션에 SVC_ONESHOT이 설정되어 있는지 체크한다. SVC_ONESHOT

옵션은 한 번만 실행하고 종료되는 프로세스에 설정된다. 따라서 이 옵션이 설정된 프로세스들은

한 번 실행 후 재시작되지 않고, kill(-pid, SIGKILL) 함수에 의해 종료된다.

❹ 프로세스가 가진 소켓 디스크립터를 모두 삭제한다.

❺ 서비스 항목이 가진 pid 값과 상태 플래그에서 프로세스가 구동중임을 나타내는 SVC_

RUNNING을 제거한다.

Page 89: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 71

❻ SVC_ONESHOT 옵션이 설정된 프로세스의 플래그에 SVC_DISABLED를 설정하고 wait_

for_one_process() 함수를 빠져나간다. 따라서 프로세스는 재시작되지 않는다.

❼ 재시작할 프로세스가 init.rc 파일에서 onrestart 옵션을 가지고 있는지 체크한다. onrestart 옵

션은 프로세스 재시작 시 실행할 명령어를 가리킨다. 코드 3-44는 onrestart 옵션이 설정된 프로

세스의 예를 나타낸다.

service servicemanager /system/bin/servicemanager user system critical onrestart restart zygote onrestart restart media

코드 3-44 | init.rc 파일에 기술된 servicemanager 서비스 정의

servicemanager는 재시작되면 on_restart()20 함수를 통해 zygote와 media 프로세스를 재시작시킨다.

❽ 마지막으로 현재 서비스 항목의 플래그에 SVC_RESTART를 추가한다. 이 플래그는 아래 코드

3-44에서 설명할 restart_processes() 함수에서 재시작할 프로세스를 결정하는 데 사용된다.

wait_for_one_process() 함수의 실행이 완료되면 이벤트 처리 루프에서 restart_processes() 함수

를 실행한다. 코드 3-45는 restart_processes() 함수를 나타낸다.

static void restart_service_if_needed(struct service *svc){ svc->flags &= (~SVC_RESTARTING); service_start(svc); return;}static void restart_processes(){ process_needs_restart = 0; service_for_each_flags(SVC_RESTARTING, restart_service_if_needed);}

코드 3-45 | restart_processes() 함수

restart_process() 함수는 서비스 리스트에서 SVC_RESTART 플래그를 가진 프로세스를 실행한다. 따

라서 자식 프로세스가 종료되어 SIGCHLD 시그널을 발생시키더라도 이 함수를 통해 재시작하게 된다.

20 http://android.git.kernel.org/?p=platform/system/core.git;a=tree의 init 디렉터리 아래의 builtins.c에 정의돼 있다.

Page 90: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

72 l 인사이드 안드로이드

3.6 프로퍼티서비스

init 프로세스의 이벤트 처리 루프에서 처리하는 또 한 가지 이벤트는 프로퍼티의 변경 요청이다. 안드

로이드 플랫폼은 시스템이 동작하는 데 필요한 각종 설정 값을 동작 중인 모든 프로세스에서 공유하

기 위해 프로퍼티라는 저장 공간을 사용하고 여기에 접근할 수 있는 API를 제공한다. 프로퍼티는 키

(key)와 값(value)으로 구성되며, ‘키 = 값’의 형태로 사용된다. 일반적인 리눅스 시스템에서는 환경변

수를 설정하고, 각 프로세스에서 설정된 환경변수의 값을 조회하여 사용하는 것이 보편적이나21 , 안

드로이드 플랫폼에서는 이를 좀더 체계적으로 사용하고, 값을 변경하는 경우 권한을 확인하는 과정

을 추가해 보안을 강화한 프로퍼티가 사용된다. 모든 동작 중인 프로세스에서는 프로퍼티의 값을 조

회할 수 있다. 그러나 프로퍼티의 값을 변경하는 것은 오직 init 프로세스만이 가능하며, init 이외의

프로세스는 init 프로세스에 변경 요청을 한 뒤 프로퍼티의 값을 변경할 수 있다. 이때 init 프로세스

는 각 프로퍼티에 대한 접근 권한을 검사한 뒤에 프로퍼티의 값을 변경한다. 프로퍼티의 값이 변경되

면 init.rc에 정의된 특정 조건을 만족하는 경우 조건에 해당하는 동작을 실행한다. 이러한 동작을 수

행하는 것을 트리거(trigger)라고 하며, “on property” 키워드에 기술된 명령이 실행된다.

property_get() 값의 조회

init 프로세스 프로세스 A

ro.secure 0

ro.allow.mock.location 1

ro.debuggable 1

persist.service.adb.enable 1

_system_property_area _

접근 권한 검사

값의 변경

트리거 실행

property_set() 값의 변경 요청

… …

Android Shared Memory공유 메모리 영역

그림 3-14 | 프로퍼티 서비스의 역할

그림 3-14는 간략하게 init 프로세스와 기타 프로세스 간에 프로퍼티 값을 조회하고 변경하는 모습

을 보여준다.

21 getenv(), setenv() 등의 함수를 사용하여 환경변수를 조회하거나 설정할 수 있다.

Page 91: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 73

3.6.1 프로퍼티 초기화

코드 3-46의 ❸에서 init 프로세스의 main() 함수 초기에 프로퍼티 영역을 초기화하기 위해

property_init()이 호출된다. property_init() 함수는 프로퍼티의 값을 저장하기 위한 공유 메모리를

생성하는데, 이를 위해 ashmem(Android Shared Memory)을 사용한다.22 외부의 프로세스들은 이

공유 메모리 영역에 접근하여 프로퍼티 값을 조회할 수 있다. 프로퍼티의 값을 변경하기 위해 공유 메

모리에 직접 접근하여 수정할 수는 없다. 대신 값을 변경하려는 프로세스에서 프로퍼티 변경 요청 메

시지를 init 프로세스에 전달하면 init 프로세스가 공유 메모리 상의 값을 변경한다.

void property_init(void){ init_property_area(); ❶ load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); ❷}

int main(int argc, char **argv){ … property_init(); ❸ …}

코드 3-46 | init.c - main() - 프로퍼티 초기화

코드 3-46의 ❸에서 property_init() 함수가 호출되고, 이 함수는 공유 메모리로 프로퍼티 영역을

생성한다. ❶에서 init_property_area() 함수가 호출된 이후 생성된 공유 메모리 영역은 그림 3-15와

같이 구성된다.

생성된 공간의 앞부분에 생성된 헤더(1024바이트 영역)에는 프로퍼티 테이블을 관리하는 데 필요

한 값이 저장되고, 나머지 31616바이트의 공간에는 247개의 프로퍼티 값을 저장하는 공간이 할당된

다. 생성된 영역에 값을 저장하거나 조회할 때는 property_get(), property_set() 함수를 이용한다.

프로퍼티 영역이 초기화되고 나면 코드 3-46의 ❷에서 파일로부터 초기값을 읽어 프로퍼티 값을 설

정한다.

22  Ashmem은 named shared memory이다. 일반적인 shared memory에서 독립적인 key를 사용하는 대신 문자열로 메모리에 대해 접

근 권한을 획득한다.

Page 92: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

74 l 인사이드 안드로이드

그림 3-15 | 프로퍼티 영역의 구조

## ADDITIONAL_DEFAULT_PROPERTIES#ro.secure=0ro.allow.mock.location=1ro.debuggable=1persist.service.adb.enable=1

그림 3-16 | default.prop 파일

그림 3-16은 에뮬레이터에서 사용되는 default.prop 파일의 내용을 보여준다. 그림에서 볼 수 있듯

이 <key> = <value> 형식으로 값이 설정돼 있다.

int start_property_service(void){ int fd; load_properties_from_file(PROP_PATH_SYSTEM_BUILD); ❶ load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); ❷

Page 93: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 75

fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); ❸ if(fd < 0) return -1; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK);

listen(fd, 8); return fd;}

int main(int argc, char **argv){ … property_set_fd = start_property_service(); ❹ …}

코드 3-47 | init.c - 프로퍼티 서비스 소켓 생성

init 프로세스는 코드 3-47의 ❹에서 start_property_service() 함수를 호출하여 프로퍼티 서비스

를 시작하는 데 필요한 유닉스 도메인 소켓을 생성하고, 생성된 소켓의 디스크립터를 저장한다. start_

property_service() 함수는 소켓을 생성하기에 앞서 코드의 ❶에서 각 파일에 저장돼 있는 프로퍼티

의 기본값을 읽어 프로퍼티 값을 설정한다. 기본적인 시스템의 초기값이 모두 설정되고 나면 코드의

❷에서 /data/property 디렉터리에 저장돼 있는 프로퍼티 값을 읽어들인다. /data/property 디렉터리

에는 시스템이 동작 중에 다른 프로세스에 의해 새로 생성된 프로퍼티 값이나, 동작중에 변경된 프로

퍼티의 값들이 저장되는데, 프로퍼티의 key가 파일 이름으로 사용되고 value는 파일의 내용으로 저장

된다. 프로퍼티의 초기값 설정이 완료되면 ❸에서 /dev/socket/property_service라는 이름의 유닉스

도메인 소켓이 생성된다.

3.6.2 프로퍼티 변경 요청 처리

앞서 생성한 소켓으로 프로퍼티 값의 변경 요청 메시지가 수신되면 handle_property_set_fd() 함수

가 호출된다.

void handle_property_set_fd(int fd){ … /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { ❶ close(s); ERROR("Unable to recieve socket options\n"); return; }

Page 94: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

76 l 인사이드 안드로이드

… switch(msg.cmd) { case PROP_MSG_SETPROP: … if(memcmp(msg.name,"ctl.",4) == 0) { if (check_control_perms(msg.value, cr.uid)) { ❷ handle_control_message((char*) msg.name + 4, (char*) msg.value); } … } else { if (check_perms(msg.name, cr.uid)) { ❸ property_set((char*) msg.name, (char*) msg.value); ❹ } … } … }}

코드 3-48 | init.c - handle_property_set_fd() - 권한 검사 및 메시지 처리

코드 3-48의 ❶은 메시지를 전송한 프로세스의 접근 권한을 검사하기 위해 소켓으로부터 SO_

PEERCRED 값을 얻어오는 부분이다. struct ucred 구조체 내부에 전송한 프로세스의 uid와 pid, gid

값이 채워진다. 이 구조체의 값을 통해 메시지의 종류에 따라 접근 권한을 검사하게 된다. 프로퍼티

메시지 중에 “ctl”로 시작되는 메시지는 실제 시스템 프로퍼티의 값을 변경하는 것이 아니라 프로세스

의 시작과 종료를 요청하는 메시지다. 코드의 ❷에서 check_control_perms () 함수를 호출하여 접근

권한을 검사하게 되는데, system server와 root, 그리고 해당 프로세스만이 ctl 메시지를 사용해서 프

로세스를 종료하거나 시작시킬 수 있다.

그 외의 메시지는 시스템의 프로퍼티를 변경하는 데 사용되고, 접근 권한은 코드 3-48의 ❸에서

check_perms() 함수를 호출하여 검사한다. 각 프로퍼티에 대한 접근 권한은 리눅스의 uid를 사용하

여 구분하며, 코드 3-49에서 볼 수 있는 바와 같이 정의돼 있다. 실제 구현하려는 시스템이 동작 중에

프로퍼티 설정을 변경할 필요가 있다면 각 프로퍼티에 대한 접근 권한을 고려하여 구현해야 한다.

struct { const char *prefix; unsigned int uid;} property_perms[] = { { "net.rmnet0.", AID_RADIO }, { "net.gprs.", AID_RADIO }, { "ril.", AID_RADIO }, { "gsm.", AID_RADIO }, { "net.dns", AID_RADIO },

Page 95: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

03 init 프로세스 l 77

{ "net.", AID_SYSTEM }, { "dev.", AID_SYSTEM }, { "runtime.", AID_SYSTEM }, { "hw.", AID_SYSTEM }, { "sys.", AID_SYSTEM }, { "service.", AID_SYSTEM }, { "wlan.", AID_SYSTEM }, { "dhcp.", AID_SYSTEM }, { "dhcp.", AID_DHCP }, { "vpn.", AID_SYSTEM }, { "vpn.", AID_VPN }, { "debug.", AID_SHELL }, { "log.", AID_SHELL }, { "service.adb.root", AID_SHELL }, { "persist.sys.", AID_SYSTEM }, { "persist.service.", AID_SYSTEM }, { NULL, 0 }};

코드 3-49 | property_service.c - struct property_perms - 접근권한

마지막으로 코드 3-48의 ❹에서 property_set() 함수가 호출되어 프로퍼티 값을 변경하고, 문제가

없다면 property_changed() 함수가 호출된다. init.rc 파일에는 프로퍼티가 변경됐을 때 필요한 동작

이 기술돼 있다. 이러한 동작이 실행되는 조건은 “on property:<key>=<value>” 형식으로 기술되며,

조건에 해당하는 키의 값이 설정되면 조건에 따른 명령(trigger)을 실행한다. 그림 3-18에 init.rc 파일

에 정의된 프로퍼티 트리거를 살펴보자. ro.kernel.qemu 프로퍼티 값이 1로 설정될 경우 adbd 서비스

를 시작하게 된다.

# adbd on at boot in emulatoron property:ro.kernel.qemu=1 start adbd

on property:persist.service.adb.enable=1 start adbd

on property:persist.service.adb.enable=0 stop adbd

그림 3-18 | init.rc 파일에 기술된 프로퍼티 트리거

Page 96: 인사이드 안드로이드 : 안드로이드 프레임워크 동작 원리 분석

78 l 인사이드 안드로이드

3.7 정리

기존 임베디드개발자들은 특정 타겟에 리눅스를 포팅하기 위해 커널 내부와 디바이스 드라이버의 개

발에 대해 고심해야 했다. 하지만 안드로이드가 등장하고부터는 커널의 포팅을 비롯해서 루트 파일 시

스템에 대한 포팅도 고심하기 시작했다. 루트 파일 시스템 포팅시 가장 먼저 만나게 되는 일이 init 프

로세스가 제대로 실행되지 않고 종료되는 문제다. 이는 디바이스 노드 파일이 적절하게 생성하지 않

았거나, init.rc 의 내용을 잘못 기술했거나 하는 등의 init 프로세스의 동작을 잘 이해하지 못해 발생

하는 문제들이다. 본 장에서 살펴본 내용을 토대로 init 프로세스가 어떻게 동작하는지 정확히 파악

함으로써 안드로이드가 부팅되는 모습만 며칠째 지켜보는 개발자들의 고생이 조금이라도 줄어들길

바란다.