Search Results for 'WIPI'

4 POSTS

  1. 2007.02.07 답답한 WIPI Emulator 1
  2. 2007.01.27 WIPI C의 메모리 할당: 과유불급
  3. 2006.12.26 WIPI 플랫폼에 포팅하기 2
  4. 2006.12.22 마이크로소프트웨어 1월호 원고

답답한 WIPI Emulator

Posted 2007. 2. 7. 02:38
원래 모바일 쪽 개발을 하지는 않지만, 최근에 WIPI 플랫폼에서 C 언어로 작은 프로젝트를 하나 하고 있습니다. 실제 폰 환경에 돌려보기 전에 윈도 환경에서 WIPI 에뮬레이터로 테스트를 먼저 하고 있는데, ETRI에서 제작한 이 윈도 WIPI 에뮬레이터가 정말 걸작(?)이네요.

1. 짤리는 로그

WIPI C의 MC_knlPrintk()라는 함수를 이용해 콘솔에 로그를 찍어보고 있는데, WIPI 에뮬레이터는 별도의 로그 창을 띄어줍니다. 근데 문제는 로그 창을 넘어가는 로그를 그대로 잘려서 보인다는 것이죠. 스크롤 바도 없어서 잘린 로그를 확인할 수 있는 방법이 없습니다.

사용자 삽입 이미지

2. 엉터리 MC_knlSprintk()

WIPI C의 MC_knlSprintk() 함수는 stdio.h의 sprintf() 함수와 동일합니다. WIPI C 환경은 stdio.h 헤더 파일을 제공하지 않기 때문에 별도의 함수를 두고 있습니다. ETRI의 윈도 에뮬레이터는 이 함수 구현이 버그투성이더군요. MC_knlPrintk(buf, "%02x", 0xff)와 같이 포맷 지정자에 %02x 와 같은 포맷이 들어가면 아무 것도 찍지 않는 문제가 있더군요. MC_knlPrintk(buf, "%02x\n", 0xff)와 같이 개행 문자(new line)이 들어가면 또 출력이 됩니다.

WIPI C 표준안에 따르면 MC_knlSprintk()는 C 언어 표준에 정의된 sprintf()을 따라야 한다고 했는데, 윈도 에뮬레이터는 윈도가 기본으로 제공하는 sprintf() 함수를 두고 왜 엉터리 구현을 굳이 작성해서 사용자를 골탕 먹이는 걸까요?


3. 서버 소켓 지원

WIPI C에서 서버 소켓 지원은 선택 사항입니다. 덕분에 일부 플랫폼에 서버 소켓을 지원할 수도 있고, 상황이 여의치 않는 경우는 지원 안 할 수도 있습니다. 확인해보니 이 표준안에 충실해서 윈도 에뮬레이터도 서버 소켓을 지원 안 하더군요. 문제는 윈도 에뮬레이터가 왜 서버 소켓을 지원 안 하는지를 모르겠다는 것입니다. 내부적으로 WINSOCK을 썼을텐데, 함수 한 개 매핑만 해주면 될 일을 굳이 선택 사항이니깐 안 해도 되겠지라는 식으로 구현해줘서, 에뮬레이터 사용자를 불편하게 만드네요.


답답한 환경에서 개발하는 모바일 개발자 여러분 힘내십시오.

WIPI C의 메모리 할당: 과유불급

Posted 2007. 1. 27. 11:18
WIPI C는 핸드폰 환경에서 C 언어로 응용 프로그램을 제작하기 위한 국내 표준 모바일 환경입니다.  메모리 할당해제, LCD 제어, 이벤트 처리, 네트워크 접속 등에 관련된 각종 API도 표준으로 정하고 있습니다. 최근에 WIPI C로 간단한 프로젝트를 할 일이 있었는데, 이 과정에서 WIPI C의 느낀 문제점을 정리해 보려 합니다. (이전 글인 "WIPI 플랫폼에 포팅하기"에서 메모리 할당에 대해 이야기한 적이 있습니다.)

*** WIPI C에서 메모리 할당/해제

WIPI C는 대부분의 메모리가 제약된 임베디드 환경에 맞추어서 메모리 컴팩션(compaction)을 기본으로 지원합니다. 기존의 malloc/free 방식은 아무리 잘 구현해도 외부 단절화(external fragmentation) 현상으로 인해 메모리를 할당 받을 수 없는 문제를 피해갈 수 없기 때문입니다.

WIPI C는 MC_knlAlloc()/MC_knlCalloc() 함수를 통해 메모리를 할당 받고 MC_knlFree() 통해 메모리를 해제합니다. 이 때 할당 받은 메모리는 포인터가 아닌 메모리 식별자(M_Uint32 타입)입니다. MC_GETDPTR()라는 매크로를 이용하면 해당 식별자에서 메모리 포인터를 얻어올 수 있습니다. 이렇게 간접 참조를 사용하는 이유는, 컴팩션이 일어나면 메모리 포인터가 언제든지 바뀔 수 있기 때문입니다. 컴팩션이 일어날 수 있는 시점 이후에는 항상 MC_GETDPTR()를 이용해 새로 포인터를 얻어오는 방식이죠.

응용 프로그램 개발자가 이 규칙을 제대로 준수하면, malloc/free 구현에서 발생하는 외부 단편화 현상도 없애고 메모리를 매우 효율적으로 이용할 수 있으므로 좋은 일이 될 것입니다. 하지만 ‘개발자 편의성’이라는 측면에서 보면 이 방식은 너무 귀찮습니다. MC_knlAlloc()/MC_knlCalloc()을 통해 메모리를 한 번 할당해 올 때마다, 컴팩션이 일어날 수 있으므로 이전에 얻어놨던 포인터가 전부 무효가 됩니다. 한 번이라도 포인터를 새로 얻어오지 않은 지점이 있으면 잠재적인 버그로 이어지니 무시무시한 것이지요.

불편함은 여기에서 그치지 않습니다. M_Int32 foo(M_Byte *str, M_Int32 len)과 같은 함수를 구현한다고 생각해 봅시다. 만약 foo라는 함수를 작성할 때 새로 메모리를 할당 받을 필요가 있으면 어떻게 될까요? 우리는 인자로 넘어온 str이 동적 할당된 메모리에서 온 것이지 정적 할당된 메모리에서 온 것인지 알 방법이 없습니다. 만약 동적으로 할당된 메모리에서 왔다면 우리가 foo() 함수 내부에서 메모리를 새로 할당 받는 순간 str은 더 이상 유효한 포인터가 아니게 될지도 모릅니다.


M_Int32 foo(M_Byte *str, M_Int32 len)
{
    M_Uint32 hnd;
    hnd = MC_knlAlloc(CONSTANTS); /* 이 시점에서 str은 더 이상 유효한 포인터가 아님 */
    // ...
}


물론 여기에 대한 대안은 포인터 대신에 식별자를 넘기도록 수정하는 것입니다. M_Int32 foo(M_Uint32 strHnd, M_Int32 len)와 같이 수정하면 foo() 함수 내부에서 새로 메모리를 할당 받더라도 변경된 포인터를 얻어올 수 있을 것입니다. 하지만 이 방식에서는 첫 번째 인자가 M_Byte *라는 타입 정보가 날라가는 효과가 있습니다. 함수만 봐서는 인자로 어떤 타입을 받는지 알 수 없는 거죠.

나름 좋은 의도로 출발한 제약 사항이라도 개발자의 편의를 심각하게 저해하는 경우 역효과를 일으킬 수 있습니다. WIPI C 개발자는 동적 할당의 제약 사항을 피하고자 되도록이면 동적 할당을 피하고, 정적 할당을 사용하는 것이 첫 번째입니다. 동적 할당에서 포인터를 매번 새로 얻어오는 번거로움을 피하고자, 포인터가 변경되지 않는 정적 할당을 필요보다 훨씬 더 많이 사용하게 되는 것이지요.

이 방식이 극단으로 가면 큰 메모리 블록은 할당 받아서 내부적으로 malloc/free 함수를 구현해 쓸 수도 있는 일입니다. WIPI C 플랫폼이 그토록 피하고자 했던 일을 개발자가 오히려 추가적인 수고를 들여서라도 하려고 하는 것입니다. 욕심이 지나치면 일을 망친다고 하였습니다. ’개발자 편의성’을 무시하고 무리한 일을 요구하면, 그 의도가 아무리 좋아도 일을 망치는 법이 아닐까 싶습니다.

WIPI 플랫폼에 포팅하기

Posted 2006. 12. 26. 02:26
WIPI 플랫폼 포팅의 일반적인 문제점

일반적인 응용 프로그램을 WIPI 플랫폼에 이식하는 데는 여러 가지 어려움이 따른다. 포팅의 어려움은 기존 플랫폼과 WIPI 플랫폼의 차이에서 기인하는데, 그 이유로 (1) 멀티 쓰레드 지원의 부재, (2) 컴팩션 하는 메모리 사용을 들 수 있다.

(1) 멀티쓰레드 지원이 없다.

WIPI-Java는 자바 언어(java.lang.Thread 클래스)를 통해 멀티쓰레드를 제공하는 데 비해서 WIPI-C는 멀티쓰레드를 지원하지 않는다. 따라서 멀티쓰레드로 작성된 프로그램을 WIPI로 옮기기 위해서는 많은 수정이 필요하다.

(2) 컴팩션(compaction)하는 메모리를 사용한다.

WIPI에서 동적으로 할당된 메모리의 컴팩션을 지원한다. 따라서 MC_knlAlloc(), MC_knlCalloc(), MC_knlFree()는 포인터가 아닌 메모리 식별자(ID)를 사용한다. MC_GETDPTR(mID)를 사용하면 메모리 식별자를 통해 메모리 포인터를 얻어올 수 있다. 메모리가 컴팩션이 일어나면 식별자를 통해 얻어온 포인터가 더 이상 유효하지 않을 수 있으므로, MC_GETDPTR()를 호출해 포인터를 다시 얻어와야 한다.

컴팩션이 일어나는 경우는 (1) 미사용 메모리가 없는 경우와 (2) MC_knlGetFreeMemory() API를 호출했을 때, (3) 내부적으로 메모리를 할당하는 일부 API를 호출했을 때뿐이므로, 매번 포인터를 얻어와야 하는 번거로움은 피할 수 있다. 하지만 (1), (2), (3)의 경우로 컴팩션이 일어날 수 있는 API를 호출한 후에는 반드시 MC_GETDPTR()를 통해 포인터를 다시 얻어와야 한다.

일반적인 응용 프로그램은 malloc()/free()와 유사한 동적 할당을 사용하고 메모리 식별자가 아닌 포인터를 직접 얻어온 후에, 해제할 때까지 영구적으로 사용한다. 따라서 이런 프로그램을 WIPI 플랫폼으로 옮길 때는 수정이 필요하다. 수정은 보통 다음 2가지 방법으로 이루어질 수 있다.

첫 번째 방법은 포팅할 응용 프로그램의 메모리 할당 방식을 WIPI의 방식으로 변경하는 것이다. 이는 응용 프로그램의 코드를 상당히 많이 고쳐야 하는 문제가 있다. 두 번째 방법은 WIPI의 MC_knlAlloc() 혹은 MC_knlCalloc()을 이용해 큰 메모리 블록을 얻어온 후에(혹은 정적 메모리 할당 이용) 응용 프로그램 내부에서 malloc()/free()와 유사한 동적 메모리 관리 알고리즘을 구현해서 사용하는 것이다. 하지만 WIPI의 일부 API는 여전히 메모리 식별자를 인자로 요구하므로, 문제를 100% 해결할 수는 없다.

WIPI란?

WIPI(Wireless Internet Platform for Interoperability)는 이동통신 단말기용 응용 프로그램 실행환경을 표준화한 규격이다. 쉽게 말하면, 국내에서 SKT, KTF, LGT 핸드폰에 공통적으로 동작하는 응용 프로그램을 작성하기 위해서는 위피 표준 규격을 따르면 된다는 뜻이다. 규격 제정 이전에는 SK의 GVM, SK-VM과 KTF의 MAP, BREW, LGT의 CLDC/MIDP 등의 플랫폼이 각기 따로 존재하였다. 때문에 응용 개발자 입장에서는 하나의 응용 프로그램을 배포하려면 무수한 포팅 과정을 거쳐야했다. WIPI는 이런 상황을 완화하고 포팅의 어려움을 덜어주고자 제안된 통합된 플랫폼이다. 현재 위피는 WIPI-자바와 WIPI-C라는 두 가지 언어 환경을 제공하고 있고 일련의 API를 표준화하여 제공하고 있다.


(1)에서 언급한 쓰레드 지원 문제는 WIPI-C의 구조상 쉽게 해결하기 힘들다. pthread와 유사한 쓰레드 라이브러리를 제공한다고 해도, 멀티쓰레드 프로그래밍은 WIPI의 메모리 모델과 충동하기 때문이다. 앞서 언급한 것처럼 WIPI는 컴팩션 메모리를 사용하고, 메모리 식별자로부터 포인터를 얻어와서 메모리에 접근한다. 기존의 단일 쓰레드 WIPI 프로그램의 경우 컴팩션이 일어나는 지점이 명확히 정의되어 있으므로, 메모리 포인터를 다시 얻어와야 하는 지점도 명확했다. 하지만 멀티쓰레드가 되면 프로그램 수행 도중 다른 쓰레드에 의해 언제든지 컴팩션이 일어날 수 있다. 따라서 메모리 포인터를 얻어올 때 다른 쓰레드가 컴팩션을 하지 못하도록 락(lock)을 잡는 일이 추가로 필요하다. 이렇게 되면 WIPI 플랫폼을 전반적으로 수정해야 하므로 적절한 방법이라 할 수 없다.

이렇게 되면 왠만한 프로그램을 WIPI로 가져오는 일은 절대 쉽지 않다는 사실을 알 수 있다.  WIPI에서 C언어 지원은 레거시 코드(legacy code)를 잘 가져와서 쓰자는 데 있다. 하지만 WIPI-C 플랫폼이 임베디드라는 특수한 환경을 고려하면서 둔 몇 가지 제약 사항이 이식성을 상당히 희생시켰다. 덕분에 모바일 환경에서 C 언어를 지원하려고 했던 원래의 취지와는 잘 맞지 않게 된 것은 아닐까?

마이크로소프트웨어 1월호 원고

Posted 2006. 12. 22. 05:48
마감이 쫓기다가 겨우 마소 1월호 원고를 마감했습니다. 1월의 주제는 "포팅의 어려움"입니다.

우리가 일상적으로 사용하는 소프트웨어의 상당수는 윈도, 리눅스, 맥 OS X 등 다양한 플랫폼에서 동작하고 있다. 자바가상머신에 의해서 처음부터 멀티플랫폼(혹은 크로스 플랫폼이라고 불림)을 보장받는 자바 어플리케이션뿐만 아니라, 일반적인 C/C++ 프로그램도 여러 플랫폼을 지원하는 경우가 드물지 않게 되었다. 일례로, 대표적 웹브라우저인 파어어폭스(Firefox)는 윈도, 맥OS X, 리눅스 세 가지 버전을 동시에 배포하고 있다. 과거에는 멀티플랫폼을 지원하는데 드는 노력에 비해 추가적으로 얻을 수 있는 사용자 수가 적었기 때문에, 멀티플랫폼 지원은 막연한 환상에 그쳤다. 하지만 근래의 소프트웨어 기술의 발전 덕분에 멀티플랫폼 소프트웨어는 작은 비용으로 조금이라도 많은 사용자를 확보할 수 있는 수단이 되고 있고, 덕분에 많은 개발자들이 관심을 가지게 되었다. 이 글에서는 멀티플랫폼 소프트웨어 작성의 근간이 되는 포팅 레이어(porting layer)에 대해 살펴보자.

포팅 레이어의 예로

1) CDC JavaVM의 HPI(Host Programming Interface)
2) 아파치 웹서버의 APR(Apache Portable Runtime)
3) WIPI의 HAL(Hardware Abstraction Layer)

을 소개하고 있습니다.