검색결과 리스트
글
안드로이드 프레임워크 프로그래밍(19) [Native 단계의 ServiceManager 동작원리]
지금까지 우리는 안드로이드 내의 System service가 ServiceManager에 의해 관리되고 있음을 알 수 있었습니다. IServiceManager를 통해 BpServiceManager를 생성하여 Binder를 통해 서비스를 등록하거나 찾는 과정 또한 확인했습니다.
/frameworks/native/libs/binder/IServiceManager.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | android::sp<IServiceManager> IServiceManager::asInterface( const android::sp<android::IBinder>& obj) { android::sp<IServiceManager> intr; if (obj != NULL) { intr = static_cast<IServiceManager*>( obj->queryLocalInterface( IServiceManager::descriptor).get()); if (intr == NULL) { intr = new BpServiceManager(obj); } } return intr; } | cs |
이러한 과정을 거치는 한 편으로는 이런 생각을 가지신 분들도 계시리라 생각합니다.
"BpServiceManager와 BnServiceManager가 존재한다면 ServiceManager 클래스도 존재하지 않을까?"
대부분의 분들이라면 분명 있으리라 생각하실 겁니다. 그러나 놀랍게도 ServiceManager는 Java 단계에서는 클래스가 존재합니다만 Native 단계에서는 ServiceManager 클래스를 확인하실 수 없습니다. 그렇다면 Native 단계에서는 ServiceManager가 사용되지 않는걸까요?
사실 Native 단계에서 ServiceManager는 daemon 프로세스와 같이 백그라운드에서 지속적으로 동작하는 프로세스로 존재합니다. 비록 ServiceManager라는 이름은 아니지만 Binder를 등록하거나 검색을 할 수 있는 기능을 갖추어 놓고 있습니다. ServiceManager는 다음과 같은 파일들로 구성되어 있습니다.
/frameworks/native/cmds/servicemanager/binder.h
/frameworks/native/cmds/servicemanager/binder.c
/frameworks/native/cmds/servicemanager/service_manager.c
시작하기에 앞서 Android.mk에 설정된 모습을 보도록 하겠습니다.
/frameworks/native/cmds/servicemanager/Android.mk
1 2 3 4 5 6 7 8 9 10 11 12 | LOCAL_PATH:= $(call my-dir) #include $(CLEAR_VARS) #LOCAL_SRC_FILES := bctest.c binder.c #LOCAL_MODULE := bctest #include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SHARED_LIBRARIES := liblog LOCAL_SRC_FILES := service_manager.c binder.c LOCAL_MODULE := servicemanager include $(BUILD_EXECUTABLE) | cs |
이제 ServiceManager가 실행되는 모습을 보도록 합시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void *svcmgr_handle; int main(int argc, char **argv) { struct binder_state *bs; void *svcmgr = BINDER_SERVICE_MANAGER; bs = binder_open(128*1024); if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } svcmgr_handle = svcmgr; binder_loop(bs, svcmgr_handler); return 0; } | cs |
/frameworks/native/cmds/servicemanager/binder.h
1 2 | /* the one magic object */ #define BINDER_SERVICE_MANAGER ((void*) 0) | cs |
처음엔 binder_state 구조체 변수와 void 변수 svcmgr이 선언됩니다. binder_state에는 이름 그대로 바인더의 상태를 저장하기 위해 사용되는 구조체임을 알 수 있습니다. 여기서 main() 함수의 내용을 하나씩 살펴보겠습니다.
bs = binder_open(128*1024);
binder_open() 함수가 선언되어 있고 이를 통해 binder_state 구조체를 return 하는 모습을 보이고 있습니다. binder_open() 함수를 통해 binder가 설정되며 인자로 메모리에 할당할 용량을 설정합니다.
/frameworks/native/cmds/servicemanager/binder.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | struct binder_state { int fd; void *mapped; unsigned mapsize; }; struct binder_state *binder_open(unsigned mapsize) { struct binder_state *bs; bs = malloc(sizeof(*bs)); if (!bs) { errno = ENOMEM; return 0; } bs->fd = open("/dev/binder", O_RDWR); if (bs->fd < 0) { fprintf(stderr,"binder: cannot open device (%s)\n", strerror(errno)); goto fail_open; } bs->mapsize = mapsize; bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); if (bs->mapped == MAP_FAILED) { fprintf(stderr,"binder: cannot map device (%s)\n", strerror(errno)); goto fail_map; } /* TODO: check version */ return bs; fail_map: close(bs->fd); fail_open: free(bs); return 0; } | cs |
binder_open() 함수의 내부를 살펴보도록 합시다. open() 함수가 호출됨으로서 바딘더의 File descriptor를 얻어낸 후 mmap() 함수를 호출하여 실제 메모리에 바인더를 할당하는 작업을 진행합니다. 모든 것이 완료되면 binder_state를 저장한 구조체 변수의 포인터를 return 합니다.
binder_become_context_manager(bs)
binder_state의 값을 통하여 컨텍스트 매니저(ServiceManager)을 설정해줍니다.
/frameworks/native/cmds/servicemanager/binder.c
1 2 3 4 | int binder_become_context_manager(struct binder_state *bs) { return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); } | cs |
binder.h
1 | #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int) | cs |
해당 함수는 ioctl() 함수를 사용하여 바인더의 입출력을 제어합니다.
binder_loop(bs, svcmgr_handler);
이 함수가 실행됨으로서 ServiceManager의 실행은 loop에 들어갑니다. 즉, 시스템에 특별한 이상이 발생하지 않는 한 작동이 계속 되는 것으로 이해하시면 좋을 듯 합니다.
/frameworks/native/cmds/servicemanager/binder.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | void binder_loop(struct binder_state *bs, binder_handler func) { int res; struct binder_write_read bwr; unsigned readbuf[32]; bwr.write_size = 0; bwr.write_consumed = 0; bwr.write_buffer = 0; readbuf[0] = BC_ENTER_LOOPER; binder_write(bs, readbuf, sizeof(unsigned)); for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (unsigned) readbuf; res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); break; } res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func); if (res == 0) { ALOGE("binder_loop: unexpected reply?!\n"); break; } if (res < 0) { ALOGE("binder_loop: io error %d %s\n", res, strerror(errno)); break; } } } int binder_write(struct binder_state *bs, void *data, unsigned len) { struct binder_write_read bwr; int res; bwr.write_size = len; bwr.write_consumed = 0; bwr.write_buffer = (unsigned) data; bwr.read_size = 0; bwr.read_consumed = 0; bwr.read_buffer = 0; res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { fprintf(stderr,"binder_write: ioctl failed (%s)\n", strerror(errno)); } return res; } int binder_parse(struct binder_state *bs, struct binder_io *bio, uint32_t *ptr, uint32_t size, binder_handler func) { int r = 1; uint32_t *end = ptr + (size / 4); while (ptr < end) { uint32_t cmd = *ptr++; #if TRACE fprintf(stderr,"%s:\n", cmd_name(cmd)); #endif switch(cmd) { case BR_NOOP: break; case BR_TRANSACTION_COMPLETE: break; case BR_INCREFS: case BR_ACQUIRE: case BR_RELEASE: case BR_DECREFS: #if TRACE fprintf(stderr," %08x %08x\n", ptr[0], ptr[1]); #endif ptr += 2; break; case BR_TRANSACTION: { struct binder_txn *txn = (void *) ptr; if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) { ALOGE("parse: txn too small!\n"); return -1; } binder_dump_txn(txn); if (func) { unsigned rdata[256/4]; struct binder_io msg; struct binder_io reply; int res; bio_init(&reply, rdata, sizeof(rdata), 4); bio_init_from_txn(&msg, txn); res = func(bs, txn, &msg, &reply); binder_send_reply(bs, &reply, txn->data, res); } ptr += sizeof(*txn) / sizeof(uint32_t); break; } case BR_REPLY: { struct binder_txn *txn = (void*) ptr; if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) { ALOGE("parse: reply too small!\n"); return -1; } binder_dump_txn(txn); if (bio) { bio_init_from_txn(bio, txn); bio = 0; } else { /* todo FREE BUFFER */ } ptr += (sizeof(*txn) / sizeof(uint32_t)); r = 0; break; } case BR_DEAD_BINDER: { struct binder_death *death = (void*) *ptr++; death->func(bs, death->ptr); break; } case BR_FAILED_REPLY: r = -1; break; case BR_DEAD_REPLY: r = -1; break; default: ALOGE("parse: OOPS %d\n", cmd); return -1; } } return r; } | cs |
binder.h
1 2 3 4 5 6 7 8 9 10 | struct binder_write_read { signed long write_size; signed long write_consumed; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ unsigned long write_buffer; signed long read_size; signed long read_consumed; unsigned long read_buffer; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ }; | cs |
제 이전 포스팅에서 Native 단계에서의 Parcel의 전송과정에 대해 다루었던 과정을 보셨던 분이라면 위의 소스코드를 읽었을 때 '앗!'이라는 생각을 하시는 분들이 계시리라 생각합니다. 그렇습니다. Parcel에서 파일이 전송되려 할 때 transact() 함수를 통해 전송되어 오는 Parcel의 값들을 처리하고 있는 것임을 본 포스팅을 통해 확실히 알게 되셨으리라 생각합니다.
사실 위의 과정에서 좀 더 많은 설명을 해드리고 싶습니다만 글을 더 진행하기엔 포스팅의 분량도 많아질 뿐더러 내용 또한 어려워지기 때문에 이후 시간이 된다면 Parcel이 Binder와 어떻게 동작하는 지에 대해 자세히 다루어 보는 시간을 가져보도록 하겠습니다.
'안드로이드 > 프레임워크' 카테고리의 다른 글
Java JNI 코드 분석 : GetObjectClass() (0) | 2015.04.29 |
---|---|
안드로이드 프레임워크 프로그래밍(20) [JNI를 통해 Native에서 JAVA 변수 및 클래스 객체 다루기] (0) | 2015.04.28 |
안드로이드 프레임워크 프로그래밍(18) [Native에서 원하는 ServiceManager 불러오기] (0) | 2015.04.02 |
안드로이드 프레임워크 프로그래밍(17) [Binder 드라이버 호출과정] (0) | 2015.04.01 |
안드로이드 프레임워크 프로그래밍(16) [Native 단계에서의 Parcel 전송(2)] (0) | 2015.03.31 |