WIPIC의 폐쇄적인 운영때문에 메모리 관리에 대해서 여러가지 설이 난무한거 같다. 실제 검색을 해봐도 명확하게 어떻게 돌아간다는 것도 없고, 추측 + 사용경험기 정도.. 그래서 나름 정리를 한번 해봐야겠다. 나와 비교도 않되는 내공으로 잘 정리해 놓은 곳도 많이 있지만, 나름 내 시각으로 정리 해 놓으면 나한테도 도움이 될 것 같아서 일단 고고싱~ 포인터 변수 = (포인터캐스팅) MC_GETDPTR(MC_knlAlloc(메모리할당사이즈));
기준은 SKT Innoace WIPI-C로 하고, KTF, LGT쪽은 아직 잘 모르겠고, compaction이 일어난다는 얘기도 있고, 아무래도 개발자들이 SKT 보다는 숫자가 더 적어서 폐쇄적이여서 더더욱 추측이 난무하는 것 같다.(LGT가 제대로 지원 안 해주는 것도 있고)
일단 WIPIC에서 new, delete를 쓰면 폰이 바로 뻗는다. 심지어 C++을 쓰면 온전한 동작을 보장 못한다고 한다. free하고 malloc은 각각 WIPI 함수 MC_knlFree, MC_knlAlloc으로 대체가 되지만 일반적인 C spec 상으로 사용하면 에러난다.(여기서부터 슬슬 짜증이 나기 시작했었다 ㅎㅎ)
일단 천천히 한번 정리 해보자
1. alloc
C상에서의 alloc은 해당 메모리를 잡아주고는 그 메모리의 포인터를 넘겨주는 것이다. 뭐 WIPI도 마찬가지다. 개발자보고 직접 메모리를 잡으라고 하지는 않는다. 문제는 retrun값. C에서의 return 값은 void*로 메모리 주소 값을 그대로 넘겨준다. 하지만 불행히도 WIPI는 메모리 주소값을 개발자에게 그렇게 쉽게 넘겨 주지 않는다
위의 코드를 보면, 실제 C에서 malloc 하는 것과 동일한 효과를 내는 wipic 함수 예를 보여준다. 일단 MC_knlAlloc이 하는 일은 메모리를 할당해 준다. 하지만 그 retrun 값이 메모리 주소 값이 아닌 WIPIC에서의 memory index 값이다. 즉 실제 alloc을 통해서 메모리번지 0x0000ff에다가 메모리를 할당 하였으면, C 함수의 경우는 0x0000ff를 return 하지만, 우리의 WIPIC 함수는 지들만 알아 볼 수 있는 0x000001을 return 한다. 실제 메모리 할당은 0x0000ff에 이루어 졌으므로, 0x000001을 return 받아서 포인터로 사용하게 되면, 엉뚱한 메모리를 접근 하므로 당연히 실행시에 에러가 난다.
WIPIC가 이러한 실제 메모리값이 아닌 지들만이 이해하는 값을 사용하는 이유는 일단 메모리 관리를 위해서이다. 사심 없이 그들이 주장하는 말을 그대로 인용하면, "제한된 embeded 환경에서 효율적인 메모리 관리를 위하여서 메모리 단편화를 줄이기 위한compaction을 위한 메모리 관리 시스템"이라고 한다. But SKT의 경우에는 compaction은 일어나지 않는다. 이 말에 100% 신뢰를 줄 수는 없지만, SKT 거의 모든 폰에 올라가는 모 프로그램을 만든 회사에서도 compaction을 고려하지 않고 만들었다.
이 부분이 좀 민감한 부분이라서 한번 더 정리하면, compaction이 일어나지 않는다고 어떠한 보장도 해주지 않는다. 하지만 대부분의 s/w가 그 부분을 고려하고 만들지를 않아서 믿고 개발해도 상관 없다.(하지만 스팩 상에는 분명 compaction은 일어나게 되어있다) 마지막으로 한번 더 강조하면 SKT WIPI-C에 대한 얘기이다.
결국 MC_knlAlloc에서는 할당한 메모리값이 아닌 WIPI_C에서 관리 하는 메모리ID 값을 넘겨주게 되고, 메모리ID 값에서부터 포인터 값(주소값)을 얻어 내는 함수가 바로 MC_GETDPRT이다. 그래서 위와 같이 메모리를 할당해야지 실제 포인터 값을 얻을 수 있다. 포인터를 얻은 이후에 포인터의 사용은 compaction이 일어나지 않는다고 전해하면 일반 C에서의 사용법과 동일하다
이정도의 개념을 가지지 않고 WIPI-C 프로그래밍하면 상당히 고생한다. 확실하게 이해하고 넘어가도록 하자
2. free
사실 free를 안할꺼면 위에 개념만으로 충분하다. 하지만 free 안하면 app은 초단위로 느려지게 되고, 나중에 뻑나고 문제 많다. 당연히 free 해야한다(가능하다면 memory leak 검사도 해야한다). 그럼 free는 뭘로 할까? C에서는 당연히 메모리 주소값(포인터값)으로 한다. 하지만 WIPIC는 메모리 주소값이 아닌, 메모리ID 값을 넘겨줘야 free가 된다.(여기서 또 좌절)
위에서 우리가 생성한 포인터에서는 주소값(0x0000ff)만이 들어가 있고 메모리ID(0x000001)값은 없다. 하지만 free를 하기위해서는 메모리ID값이 필요하므로, 위에서 alloc 할 때에 메모리ID값을 따로 저장했다가 free 할 경우에 해당 메모리ID 값으로 free를 하는 것이 정석이다.(MC_GETDPTR함수가 메모리ID 값을 넣으면 메모리주소값을 넘겨주는데 그 역의 함수는 없다.)
실제 아래 예제 소스에서는 그렇게 하였으나 사실 SKT에서는 다른 방법이 있다. 그 방법은 따로 한번 더 포스팅을 할 예정이다. 내공이 워낙 없어서 제대로 설명을 못하겠다. 아무튼 잠깐 맛보기만 보여주면, 실제 MC_GETDPTR()하는 부분을 따라가게 되면, 정말 재미있게도 아래와 같은 것을 볼 수 있다.
#define MC_GETDPTR(ptr) *(void**)prt
아니 이런 충격이~!! 실제 WIPIC는 메모리 테이블 관리를 저런 간단한 포인터 연산을 통해서 하고 있었다. 혼자 저 마법의 비밀을 풀어 보는 것도 좋을 것이다. 간단히 답을 말해버리면 주소 값에서 4를 빼면 된다. 더 자세한 설명은 다음에 기회가 있으면 하고, 일단은 저 마법을 풀거나, 메모리ID를 보관하고 있다가 free 할 때 보관 했던 메모리ID 값으로 MC_knlFree를 하면 된다.
/* 마법을 풀려면 WIPI Header를 참조하고 팬과 종이에 포인터 연산을 하면서 runtime에 break 포인트 걸어서 WIPI가 메모리 관리하는 걸 감상하면 쉽게 풀릴 것이다. */
3. new & delete
위의 개념을 이해 했으면 new & delete는 공짜다. 먼저 new delete를 하기 위해서는 C++이 돌아가야한다. 하지만 WIPI-C는 C++의 동작을 보장해 주지 않는다.(이 면피성 발언 ㅡㅡ++) 내가 SKT 메니저도 아니고 WIPIC 만든 사람도 아니여서 C++의 동작을 대신 보장 해 줄 수는 없지만, 마찬가지 논리로 국내 출시폰에 대부분 들어가는 핵심 S/W 만드는 회사 코드는 C++로 되어있다. 조심히 써야할 부분은 있지만 안돌아가지는 않는다.
여기서 하나 더 개념 잡고 들어 갈 것이 WIPI-C 코드는 말그대로 C코드다(WIPI-C++ 이라고 부르지 않는다). 외부적으로는 C 형식으로 불리운 다는 뜻이다. extern C 같은 키워드 잘 사용해한다(이 부분도 C와 C++에서의 함수 호출 방법때문이다. 이건 WIPI 문제가 아닌 C와 C++ 호환 문제)
아무튼 이제 C++도 사용해서 class도 만들었는데, new 하고 delete를 하면 바로 뻗는다. 그 이유는 위의 이유와 같다. new를 하면 메모리값이 아닌 메모리ID 값을 넘겨주기 때문이다. 그래서 new, delete operator도 새로 정의 하는게 여러모로 편리하다. 정의 방법은 위와 동일한 개념이며, 한번 정의 해 놓으면 상속해서 쓰면 되니까 BaseClass 형식으로 하나만 정의 해 놓으면 된다. 아래는 내가 정의 해 놓은 예제이다. 실제 소스에서 추출한 것이라서 동작이 안할 수도 있으니, 참고용으로만 쓰는 것이 건강에 좋을 것이다. 개념 자체와 코드는 몇줄 안되니 참고해서 스스로 작성해도 될 것 같다.(사실 이 소스도 웹에서 긁어 온거 좀 변형했다. copy&paste 인생 ㅋㅋ)
#include "BaseObject.h"
BaseObject::BaseObject()
{
}
BaseObject::~BaseObject()
{
}
void BaseObject::operator new(size_t a_nSize)
{
long nObjID = 0;
void pRetObj = NULL;
nObjID = MC_knlAlloc(a_nSize); // 메모리 확보
pRetObj = MC_GETDPTR(nObjID); // pointer 얻어오기
((BaseObject*)pRetObj)->SetWIPIMemID(nObjID); // 메모리 ID변수에 저장(delete 시에 free용도)
return pRetObj;
}
void BaseObject::operator delete(void* rawMemory, size_t size)
{
if(rawMemory)
{
if(((BaseObject*)rawMemory)->getWIPIMemID())
{
MC_knlFree(((BaseObject*)rawMemory)->getWIPIMemID()); // 저장해 놓은 메모리 ID로 free
}
}
}
void BaseObject::SetWIPIMemID(long nMemoryID)
{
m_nMemoryID = nMemoryID;
}
long BaseObject::getWIPIMemID()
{
return m_nMemoryID;
}
어찌 어찌 대략적으로 WIPI-C 메모리에 대해서 정리를 해보았다. 조금이라도 삽질을 줄일 수 있다면 개인적으로 큰 영광이 될꺼 같다





덧글
roess 2009/05/01 13:22 # 답글
좋은 노하우 잘 봤습니다 ^^