C언어

[APR] memory pool

고요한하늘... 2009. 1. 22. 17:51

메모리풀( apr_pool_t )

libapr APIs들 대부분은 메모리풀을 사용한다.
메모리 풀로 인해 메모리 청크를 쉽게 관리할 수 있다. 메모리 풀이 없는 시스템을 생각해보면, 메모리를 여러번 활당 받아야 한다.
물론 할당된 것들은 각각 해제를 해야 한다. 열번 메모리를 할당했다면 열번 해제를 해줘야 한다. 그밖에도 메모리 릭 버그로 고통을 격을 수도있다.
메모리 풀은 이런 문제들을 해결한다.  메모리 풀은 한번만 할당하면 메모리 풀에서 자유롭게 메모리를 할당 받아서 사용할수 있다. 메모리들을 해제하기 위해 메모리 풀만 해제 하면 된다. 여기에는 두가지 이점이 있다. 첫번째는 앞에서 말한 메모리 릭에 대한 것과 다른 하나는 메모리를  매번 할당할때 보다 비용이 상대적으로 적다는 것이다.
어떤 점에서 메로리풀은 session-oriented 프로그램을 수행하기 위해 더 없이 좋을 수 있다. 어떻게 보면 메모리 풀은 세션의 한 종류이다.  즉 오브젝트들은 모두 동일한 lifetime을 가진다. 세션 안에서 오브젝트 설정을 컨트롤 할수도 있다. 세션이 시작되면서 메모리 풀을 생성한다. 그러면 세션이 유지되는 동안 메모리 풀안에 오브젝트를 생성한다. 여기서 알아야 할것은 그 오브젝트들의 lifetime에 대해서 신경쓰지 않아도 된다는 것이다. 마지막에 세션의 끝날때에는 메모리 풀을 해제해야 한다.

주의 : 일반적으로 오브젝트의 lifetime 조정은 프로그래밍에서 가장 어려운 부분 중에 하나이다. 그래서 그런 것들을 위한  스마트 포인터와 같은(GC 등등) 여러 기술들이 있다. 기억해야 할 것은 동시에 그런 기술을 사용하기 위해서 약간 어려움이 있을수 있다.
메모리 풀은 그런 기술 중에 하나이기 때문에 이런 것들을 섞어서 사용하기 위해서는 주위가 필요하다.


기본적인 APIs를 보면

apr_status_t apr_pool_create(apr_pool_t **newpool, apr_pool_t *parent);

void * apr_palloc(apr_pool_t *p, apr_size_t size);

void apr_pool_destroy(apr_pool_t *p);


apr_pool_create()로 메모리 풀을 만든다. 메모리 풀은 apr_pool_destroy()가 호출될 때 까지 유효하다. apr_pool_create()의 첫번째 아규먼트는 결과값이다.
이 API호출하면  메모리풀 오브젝트( apr_pool_t ) 새롭게 만들어진다.
apr_palloc()은 지정한 크기의 메모리 한 블럭을 얻을 수 있다. mp-sample.c를 보면 사용법을 알 수 있을것이다.


char *buf1;
apr_pool_t *mp;

/* create a memory pool. */
apr_pool_create(&mp, NULL);

/* allocate memory chunks from the memory pool */
buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);


apr_palloc()을 malloc(3)과 같이 사용할수 있다. 그리고 할당된 메모리를 모두 0값으로 초기화한 후에 리턴하는 calloc() 과 apr_pcalloc()은 유사한 기능을 한다.
malloc()/calloc()를 사용한다면, 할당된 메모리에 대해서 free를 호출해야 한다. 반대로 메모리 풀에 있는 메모리 청크을 사용하면 각각의 메모리를 해제해줄 필요가 없다. apr_pool_destroy()를 호출함으로써 모든 메로리 청크가 반환된다.

주의: apr_palloc()로 호출할수 있는 메모리 크기에는 제한이 없다. 그럼에도 불구하고 메모리 풀에서 거대한 메모리 청크을 할당하는 것은 좋지 않을수 있다.  왜냐하면 메모리 청크은 기본적으로 작은 단위로 디자인 됐기 때문이다. 사실 메모리 풀의 초기 크기는 8kbyte이다. 큰 메모리 청크( 몇 메가 바이트 이상)가 필요하다면 메모리 풀을 사용 안하는게 좋다.

주의 : 메모리 풀 매니저는 시스템에 할당된 메모리를 반납하지 않는다. 시스템이 오랜 시간 동안 동작중이라면 아마 문제가 있는 것이다. 그래서 시간 제한을 두는게 좋다.

/* sample code to set the upper limit to make memory pool manager release the memory back to the system */
#define YOUR_POOL_MAX_FREE_SIZE 32      /* apr_pool max free list size */

apr_pool_t *mp;
apr_pool_create(&mp, NULL);
apr_allocator_t* pa = apr_pool_allocator_get(mp);
if (pa) {
    apr_allocator_max_free_set(pa, YOUR_POOL_MAX_FREE_SIZE);
}


좀 더 알아야 할 두개의 APIs가 있는데 하나는 apr_pool_clear()이고 다른 하나는 apr_pool_cleanup_register()이다. apr_pool_clear()은 apr_pool_destroy()와 비슷하다.  그러나 메모리 풀이 여전히 재사용 가능하다는 점이 다르다. 일반적인 코드를 보면


/* sample code about apr_pool_clear() */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);

for (i = 0; i < n; ++i)
{
    do_operation(..., mp);
    apr_pool_clear(mp);
}


do_operation()안에서 사용하는 메모리풀은 여러개의 메모리 청크를 할당한다.  do_operation()밖에서 메모리 청크가 필요 없으면 apr_pool_clear()을 호출한다. 그러면 메모리 최대 사용량을 줄일 수 있다. 로컬 스택 메모리 시스템에 익숙하다면 메모리 풀이 로컬 스택 메모리로 생각할 수 있다. apr_palloc()는 SP( stack pointer)를 이동하는 것과 유사하데 apr_pool_clear()은 SP를 뒤돌리는 것과 유사하다. 둘다 매우 가벼운 연산들이다.

apr_pool_cleanup_register()에서 메모리 풀의 clear/destroy 함수를 훔칠(HOOK)수 있다. 메모리 풀을 청소(clean)하거나 해제할 경우에 callback 함수를 호출한다. 메모리 풀을 관리하는 종결 함수를 callback 함수 형태로 구현할 수 있다.

마지막 토픽은 메모리풀의 서브풀이다. 각각의 메모리풀은 모두 부모(parent) 메모리풀이 있다. 따라서 메모리풀은 트리(tree)를 구성한다.  apr_pool_create()의 두번째 아규먼트는 부모(parent) 메모리풀을 의미한다. 만약 NULL로 부모 메모리 풀을 설정하면 새롭게 설정되는 메모리풀은 ROOT가 된다. 이 ROOT 에 서브(sub) 메모리 풀을 만들수도 있다. 트리(tree)로 구성된 메모리 풀에서  apr_pool_destroy()를 호출하면 자식 메모리 풀도 모두 해제된다. 메모리 풀에서 apr_pool_clear()을 호출하면 메모리가 가용상태가 되는데 자식 메모리 풀은 해제된다.
자식 메모리풀이 해제될때 위에서 언급한 cleanup 함수가 호출된다 .

주의 : 풀을 cleanup을 위한 callbak 함수를 NULL로 등록했을때 나타나는 전형적인 버그를 안고 있다. 그래서  apr_pool_cleanup_null로 검사를 한다.


/* pseudo code about memory pool typical bug */
/* apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, NULL); THIS IS A BUG */
/* FIXED */
apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, apr_pool_cleanup_null);

 

apr_pool_destroy(mp);

 

'C언어' 카테고리의 다른 글

[APR] Container APIs  (0) 2009.01.29
[APR] file handling  (0) 2009.01.23
apache 코어 설정  (0) 2009.01.22
apr Makefile 만들기  (0) 2009.01.22
Apache Portable Runtime 설치  (0) 2009.01.19