검색결과 리스트
글
안드로이드 프레임워크 프로그래밍(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 |
설정
트랙백
댓글
글
[C/C++] typedef 함수 포인터 구현원리
최근 안드로이드 프레임워크를 공부하다보니 JAVA는 물론 JNI를 통해 연결되는 C/C++ 코드들에 대해 빠삭하게 공부를 하고 있습니다. 정말이지 흔히 쓰는 저 언어들에 슬슬 도가 트고 있지 않은가 싶을정도로 자신의 실력에 대해 자만심이 들기도 할 정도입니다.
소스코드들을 공부하는 과정에서 어려운 부분이 있다면 바로 흔히 사용하지 않는 방식으로 구현된 소스코드를 해석하는 때라고 생각합니다. 특히 수업시간에는 이론만 알고 넘어가는 함수 포인터라는 생소한 개념이 쓰였을 때는 처음엔 이것의 정체 조차 모르는 경우도 허다하지요.
본론으로 들어가기에 앞서 간단한 소스코드를 통하여 함수 포인터에 대한 기념을 알아보도록 하겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include<stdio.h> void (*ptrfunc)(int); void testprint(int n){ printf("Number : %d\n", n); } int main(){ testprint(100); ptrfunc = testprint; ptrfunc(77); return 0; } | cs |
위의 결과 출력을 보시면 대략적인 함수 포인터의 동작 원리를 이해하실 수 있을 것이라 생각합니다.
여기서 잠시 코드를 좀 더 자세히 설명 드리도록 하겠습니다.
void (*ptrfunc)(int);
함수 포인터는 위에서 보시는 바와 같은 구조로 이루어져 있습니다. 각 부분의 기능은 다음과 같습니다.
return값의 자료형 (*포인터 함수의 이름) (인자값)
함수 포인터를 사용하실 때 주의하실 점은 함수 포인터가 이용하고자 하는 함수의 return값의 자료형과 인자값의 자료형 및 갯수가 일치해야 사용할 수 있다는 점입니다. 다음 코드를 확인해 봅시다.
ptrfunc = testprint;
함수 포인터에 사용하고자 하는 함수의 이름을 입력합니다. 위 과정을 통해 기존 포인터와 같이 함수의 주소값이 포인터에 저장됨으로서 해당 함수 포인터는 자신이 가지고 있는 주소값의 함수와 같은 기능을 구현하게 됩니다.
다음으로 typedef가 사용된 함수 포인터에 대해 살펴보도록 합시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include<stdio.h> typedef void (*ptrfunc)(int); void testprint(int n){ printf("Number : %d\n", n); } int main(){ testprint(100); ptrfunc elecs; elecs = testprint; elecs(77); return 0; } | cs |
위에서 설명하였던 소스코드에 typedef를 적용하여 보았습니다. typedef문이 이곳에서는 어떻게 적용되고 있는지 살펴보도록 합시다.
보시는 대로 기존에 있던 함수 포인터가 선언된 부분 앞쪽에 typedef가 선언되어 있는 모습을 보고 계십니다. typedef문은 빈번하게 사용되는 소스코드가 복잡하거나 길 경우 이를 간결하게 사용하기 위한 목적으로 사용되는데요 함수 포인터에서의 typedef문은 지금껏 보았던 typedef문과는 약간 사용되는 방법이 다르지만 결국은 사용되는 목적은 같습니다.
다음으로 typedef 함수 포인터가 응용되는 부분을 보여드리도록 하겠습니다.
ptrfunc elecs;
응용이라고 말씀드려서 뭔가 거창한 걸 하려나 하겠습니다만 사실 typedef로 선언된 함수 포인터는 위에서 보시는 바와 같이 매우 간결하게 쓰이고 있음을 아실 수 있습니다. ptrfunc로 정의된 typedef문의 함수 포인터를 elecs라는 이름의 함수 포인터 하나를 만들었다고 보시면 됩니다. 쉽게 설명해서 함수포인터인 변수 하나가 생겼다고 생각하시면 됩니다. 아직도 이해가 안되신다면 아래의 간단한 소스코드를 보시면 아하! 하고 이해하실 겁니다.
int elecs;
elecs = 199;
이제 감이 오시는지요? 그렇습니다! typedef문으로 선언된 함수 포인터는 마치 자료형을 선언하는 것과 같이 간단하게 함수 포인터 변수를 선언한다고 생각하시면 되는 것입니다! 혹시나 해서 아직도 이해하지 못하신 분들을 위해 저 위에 typedef 함수 포인터가 실제로는 어떻게 구현되어 있는지 보여드리겠습니다.
void (*elecs)(int);
위에서 보시는 바와 같이 ptrfunc 부분이 elecs로 치환된 것이라고 생각하시면 제 설명을 정확히 이해하시는 것입니다!
혹시 typedef 함수 포인터의 원리에 대해 알고자 하셔서 오신 분들이라면 포스팅을 여기까지만 읽어주셔도 자신의 실력으로 함수 포인터를 활용하실 수 있으리라 생각합니다. 아래에서 부터는 다소 어려우니 기죽지 마시고 이렇게 활용되고 있구나 하는 생각으로 읽어주셨으면 합니다.
그렇다면 이제 실전에서 사용되고 있는 코드를 보도록 하겠습니다. 아래의 소스코드는 안드로이드 내에서 구현된 함수 포인터 입니다. 언어는 C++로 구성되어 있습니다만 함수포인터를 설멍하는데 큰 어려움은 없을 것입니다. 소스는 다음과 같습니다.
/frameworks/av/camera/CameraBase.cpp
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 | template <typename TCam, typename TCamTraits> sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId, const String16& clientPackageName, int clientUid) { ALOGV("%s: connect", __FUNCTION__); sp<TCam> c = new TCam(cameraId); sp<TCamCallbacks> cl = c; status_t status = NO_ERROR; const sp<ICameraService>& cs = getCameraService(); if (cs != 0) { TCamConnectService fnConnectService = TCamTraits::fnConnectService; status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid, /*out*/ c->mCamera); } if (status == OK && c->mCamera != 0) { c->mCamera->asBinder()->linkToDeath(c); c->mStatus = NO_ERROR; } else { ALOGW("An error occurred while connecting to camera: %d", cameraId); c.clear(); } return c; } | cs |
여기서 참으로 특이한 구조의 소스코드를 만나게 되었습니다.
TCamConnectService fnConnectService = TCamTraits::fnConnectService;
이제 이 부분이 어떻게 구현되었는지 자세히 보도록 합시다.
/frameworks/av/include/camera/CameraBase.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | template <typename TCam> struct CameraTraits { }; template <typename TCam, typename TCamTraits = CameraTraits<TCam> > class CameraBase : public IBinder::DeathRecipient { public: typedef typename TCamTraits::TCamListener TCamListener; typedef typename TCamTraits::TCamUser TCamUser; typedef typename TCamTraits::TCamCallbacks TCamCallbacks; typedef typename TCamTraits::TCamConnectService TCamConnectService; .... } | cs |
위 코드를 통하여 다음과 같은 사실을 알아내었습니다.
TCamConnectService로 정의된 부분이 CameraTraits<TCam>::TCamConnectService와 동일하다는 것을 알고 다음으로 CameraTraits에 대해 확인해 보도록 하겠습니다.
/frameworks/av/include/camera/Camera.h
1 2 3 4 5 6 7 8 9 10 11 12 | template <> struct CameraTraits<Camera> { typedef CameraListener TCamListener; typedef ICamera TCamUser; typedef ICameraClient TCamCallbacks; typedef status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&, int, const String16&, int, /*out*/ sp<ICamera>&); static TCamConnectService fnConnectService; }; | cs |
위 코드에서 정말 요상하게 친구가 하나 보이는군요.
typedef status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,
위에서 배운 바와 같이 해당 코드는 typedef 함수 포인터입니다. 다만 포인터 함수의 이름이 참으로 독특한데 이는 멤버 포인터라는 C++에서 사용되고 있는 기능입니다. 멤버 포인터에 대해 좀 더 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시기 바랍니다.
http://showmiso.tistory.com/210
바로 그 아래에는 typedef로 선언된 함수 포인터에 대한 변수를 static으로 선언되었음을 확인하실 수 있습니다.
이제 여기서 다시 앞에서 확인하였던 선언문을 다시 한 번 보도록 합니다.
위 소스코드는 TCamConnectService로 선언된 typedef 함수 포인터를 가진 변수명 fnConnectService 안에 TCamTratis::fnConnectService 함수의 주소값을 넣겠다는 의미로 이해해 주시면 되겠습니다. 그렇다면 여기서 TCamTratis::fnConnectService 함수는 어떻게 구현되었는지 찾아보도록 하겠습니다.
/frameworks/av/camera/Camera.cpp
1 2 | CameraTraits<Camera>::TCamConnectService CameraTraits<Camera>::fnConnectService = &ICameraService::connect; | cs |
Camera.cpp 소스 코드 내에서 'CameraTraits<Camera>::fnConnectService' 라는 이름의 포인터 함수 변수가 선언되었고 해당 포인터 함수에 ICameraService::connect 함수의 주소를 넣어준다고 이해하시면 되겠습니다.
/frameworks/av/include/camera/ICameraService.h
1 2 3 4 5 6 7 8 9 10 11 12 | class ICameraService : public IInterface { public: .... virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16& clientPackageName, int clientUid, /*out*/ sp<ICamera>& device) = 0; .... } | cs |
/frameworks/av/camera/ICameraService.cpp
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 | class BpCameraService: public BpInterface<ICameraService> { public: BpCameraService(const sp<IBinder>& impl) : BpInterface<ICameraService>(impl) { } .... // connect to camera service (android.hardware.Camera) virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16 &clientPackageName, int clientUid, /*out*/ sp<ICamera>& device) { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); data.writeStrongBinder(cameraClient->asBinder()); data.writeInt32(cameraId); data.writeString16(clientPackageName); data.writeInt32(clientUid); remote()->transact(BnCameraService::CONNECT, data, &reply); if (readExceptionCode(reply)) return -EPROTO; status_t status = reply.readInt32(); if (reply.readInt32() != 0) { device = interface_cast<ICamera>(reply.readStrongBinder()); } return status; } .... } | cs |
드디어 우리는 connect 함수를 찾아내는 데 성공하였습니다. 이로서 함수 포인터가 정의 되는 과정을 모두 살펴보았습니다. 마지막으로 이 기나긴 여정을 코드로 간결하게 요약하자면 다음과 같습니다.
TCamConnectService fnConnectService = TCamTraits::fnConnectService;
위 코드는 아래와 같이 변동이 됨을 확인하실 수 있습니다.
status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,
TCamConnectService = &ICameraService::connect;
위의 코드를 처음 보시는 분들은 이 시점에서도 모두 이해가 안 되실 수 있습니다. 하지만 위 코드에서 일정 부분 만이라도 이해하셨다면 여러분들은 성공하신 겁니다!
'프로그래밍 팁' 카테고리의 다른 글
[C/C++]thread 조건변수 다루기 - pthread_cond_broadcast() (0) | 2015.09.01 |
---|---|
[JAVA]JDWP(Java™ Debug Wire Protocol) (0) | 2015.08.21 |
[Java] Error 혹은 Debug시 등장하는 method인 access$000 (0) | 2015.08.20 |
[JAVA]윈도 CMD를 통해 자바 Command Line 명령어 활용하기 (0) | 2014.10.04 |
[JAVA] 간단한 파일 입출력(FILE I/O) 구현 (0) | 2014.09.10 |
설정
트랙백
댓글
글
컴퓨터가 대기모드 되자마자 바로 풀릴 때 해결방법
최근에 노트북에 문제가 생긴 듯 하여 프로그램을 업데이트 하는 김에 BIOS를 최신버전으로 교체한 후 컴퓨터를 껏다 켜보았습니다. 이전과는 그다지 달라진 점은 보이지 않았습니다만 평상시 때와 같이 노트북을 닫았는데 이상하게도 노트북이 대기모드에 들어가자마자 바로 풀리는 것이 아닙니까! 그것도 노트북이 접혀있는 상태 그대로 말이지요!
참으로 골치가 아파오더군요. 기껏 새로운 BIOS를 적용시켜 줬건만 평소에 쓰던대로 못하게 된게 참으로 골치가 아파오더군요. 원인이 대체 무엇인지 알 수 없어 일단은 절전모드 설정과 관련된 자료들을 살펴보았습니다.
우선 자신의 컴퓨터에 절전모드에서 깨어나게 하는 기능들에 대해 알아보도록 합니다. 먼저 컴퓨터의 CMD창을 열어서 다음과 같이 검색해 주시면 되겠습니다. CMD는 Windows7 기준으로 시작메뉴를 누른 후 'cmd'라고 입력하시면 되겠습니다.
powercfg -devicequery wake_armed
일단 위와 같이 자신의 컴퓨터를 깨우게 되는 기능들이 쭉 나오고 있습니다. HID의 경우 거의 대부분이 자신의 컴퓨터에 연결된 마우스로 추정하시면 될 듯 합니다.
일단 제 컴퓨터의 상황상 마우스가 범인일 확률이 높아졌습니다. 안그래도 마우스가 고장나서 최근에 새로 구매했었는데 왠지 새로 구매한 마우스에 해당 기능을 가지고 있는 듯 해 보이는 것이었지요. 그래서 노트북에서 마우스의 연결을 해제한 후 절전모드에 들어가 보았습니다.
이럴수가! 역시나 마우스가 범인이었습니다! 마우스를 빼고 절전모드에 돌입하더니 노트북 덮개를 열기 전까지 컴퓨터는 절전모드를 계속 유지하고 있음을 확인하였습니다.
그렇다면 이제부터는 마우스가 연결된 상태에서도 노트북이 절전모드에서 깨어나지 않도록 한다면 문제를 해결할 수 있을 듯 합니다! 그래서 이번에는 마우스가 절전모드에 개입하지 않도록 설정하는 방법에 대해 알아보도록 합니다!
먼저 시작메뉴를 여신 후 자신의 컴퓨터 속성 메뉴로 들어갑니다. 시작메뉴를 클릭하신 후 '컴퓨터' 메뉴에서 오른쪽 마우스 클릭 후 '속성(R)'을 클릭하시거나 'Windows키 + pause' 버튼을 동시에 누르면 바로 보실 수 있습니다.
그 다음 장치괸리자로 들어갑니다.
그 다음 장치관리자에서 자신의 기기에 해당하는 부분을 마우스 오른쪽을 클릭하신 후 '속성(R)'을 선택합니다.
'전원관리' 탭으로 이동하신 후 '이 장치를 사용하기 위해 컴퓨터의 대기 모드를 종료할 수 있음(O)'의 체크를 해제하신 후 확인 버튼을 눌러줍니다.
이제 자신의 컴퓨터에서 설정된 대기모드들을 확인해 봅니다.
powercfg -devicequery wake_armed
보시는 바와 같이 HID가 해제되어있는 것을 확인하실 수 있습니다. 이제 자신의 컴퓨터의 절전모드가 문제없이 동작되는 것을 확인하실 수 있으실 겁니다!
'공대생의 팁' 카테고리의 다른 글
공유기에 연결된 Linux(Ubuntu) 컴퓨터를 외부에서 원격 데스크톱 연결을 통해 조작하기 (6) | 2015.05.07 |
---|---|
TIZEN 소스코드 빌드 gbs가 설치되지 않을 때 수동으로 설치하기 (0) | 2015.05.02 |
우분투에서 인터넷창을 통해 윈도 미디어 플레이어 관련 영상 보는 방법 (0) | 2015.04.04 |
우분투 사용중 안보이거나 깨진 언어가 나올 때 언어 설치방법 (0) | 2015.03.26 |
티스토리 블로그를 반년간 하면서... (0) | 2015.03.12 |