달력

5

« 2024/5 »

  • 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
2011. 10. 19. 00:31

kmalloc() OS이야기2011. 10. 19. 00:31

kmalloc() 함수는 유저 공간에서 많이 쓰이는 malloc() 루틴과 매우 비슷하게 동작하지만, 대신 추가적인 플래그를 매개변수로 가진다. kmalloc()은 바이트 단위로 커널 메모리를 얻는데 사용하는 간단한 메모리이다.

페이지 전체의 메모리가 필요하다면, 특히 2의 승수 크기의 메모리가 필요하다면 바로 앞에서 다룬 페이지 단위 메모리 할당 인터페이스를 사용하는 편이 더 좋을 것이다. 하지만 대부분의 커널할당에서는 kmalloc()이 더 자주 사용된다.
이 함수는 <linux/slab.h>에 선언돼있다. 

 http://lxr.linux.no/linux+v2.6.39.4/include/linux/slab.h
void *kmalloc(size_t size, int flags)
이 함수는 길이가 최소한 size바이트가 되는 메모리 영역의 포인터를 리턴한다. 할당된 메모리 영역은 물리적으로 연속적이다. 또한 에러가 발생하면 NULL을 리턴한다. 커널 할당은 가용한 메모리가 부족하지 않다면 항상 성공한다. kmalloc()을 호출한 후에는 항상 NULL을 체크하여 오류를 적절히 처리해야 한다.

struct dog *ptr;

ptr = kmalloc(sizeof(struct dog), GFP_KERNEL);
if(!ptr)
   /* 오류 처리 */

GFP_KERNEL 플래그는 메모리 할당 후 메모리를 요청한 kmalloc()의 호출자로 리턴하도록 메모리 할당자의 동작을 지정한다.

gtp_mask 플래그
이들 플래그는 세 개의 범주로 나뉘는데, 그것은 각각 동작 조절자(action modifiers), 영역 조절자(zone modifier), 유형(type)이다. 동적 조절자(action modifier)는 커널이 어떤 식으로 요청받은 메모리를 할당할지를 지정한다. 즉 어떤 상황에서는 메모리 할당에 특정한 방법만을 사용해야 한다는것이다. 예를들면, 인터럽트 핸들러는 메모리를 할당하는 과정 중에 커널이 휴면상태가 되지 않도록 지시해야 한다.
한편 영역 조절자(zone modifier)는 어느 영역에서 메모리를 할당받을 것인가를 지정한다. 앞서 살펴보았듯, 커널은 물리적 메모리를 여러가지 영역으로 구분하며 서로 다른 목적으로 사용한다. 영역 조절자는 이들 영역 중 어디에서 할당받을지를 결정한다.
유형 플래그는 특별한 형태의 메모리 할당을 우히나 동작/영역 조절자의 조합으로 되어있다. 즉 유형 플래그는 단순히 여러 가지 조절자를 모아놓은 것으로, 어떤 조절자들의 조합을 사용하는 대신 적절한 하나의 유형 플래그를 사용할 수 있다.
GFP_KERNEL은 유형플래그의 하나이며, 커널의 프로세스 컨텍스트의 코드에 사용된다. 

동작 조절자(action modifier)
동작 조절자의 모든 플래그는 <linux/gfp.h>에 포함되어 있다.

/*  55 * Action modifiers - doesn't change the zoning  56 *  57 * __GFP_REPEAT: Try hard to allocate the memory, but the allocation attempt  58 * _might_ fail.  This depends upon the particular VM implementation.  59 *  60 * __GFP_NOFAIL: The VM implementation _must_ retry infinitely: the caller  61 * cannot handle allocation failures.  This modifier is deprecated and no new
 
62 * users should be added. 63 * 64 * __GFP_NORETRY: The VM implementation must not retry indefinitely. 65 * 66 * __GFP_MOVABLE: Flag that this page will be movable by the page migration 67 * mechanism or reclaimed 68 */ 69#define __GFP_WAIT ((__force gfp_t)___GFP_WAIT) /* 할당자는 휴면할 수 있다. */ 70#define __GFP_HIGH ((__force gfp_t)___GFP_HIGH) /* 할당자가 emergency pool에 접근할 수 있다? */ 71#define __GFP_IO ((__force gfp_t)___GFP_IO) /* 할당자가 디스크 IO를 시작할 수 있다. */ 72#define __GFP_FS ((__force gfp_t)___GFP_FS) /* 할당자가 파일시스템 IO를 시작할 수 있다.*/ 73#define __GFP_COLD ((__force gfp_t)___GFP_COLD) /* 할당자는 반드시 캐쉬 콜드(cache cold)페이지를 사용해야 한다.*/ 74#define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN) /* 할당자는 실패 경고 메시지를 출력하지 않는다. */ 75#define __GFP_REPEAT ((__force gfp_t)___GFP_REPEAT) /* 할당자는 실패시 재시도한다. 하지만 실패할 수도 있다. */ 76#define __GFP_NOFAIL ((__force gfp_t)___GFP_NOFAIL) /* 할당자 무한히 할당작업을 반복한다. 할당이 실패할수없다. */ 77#define __GFP_NORETRY ((__force gfp_t)___GFP_NORETRY) /* 할당자는 실패할 경우 재시도하지*/ 78#define __GFP_COMP ((__force gfp_t)___GFP_COMP) /* 복잡 페이지 메타데이터를 추가한다. hugetlb 코드에서 내부적으로 사용한다.*/ 79#define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) /* Return zeroed page on success */ 80#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves */ 81#define __GFP_HARDWALL ((__force gfp_t)___GFP_HARDWALL) /* Enforce hardwall cpuset memory allocs */ 82#define __GFP_THISNODE ((__force gfp_t)___GFP_THISNODE)/* No fallback, no policies */ 83#define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) /* Page is reclaimable */ 84#define __GFP_NOTRACK ((__force gfp_t)___GFP_NOTRACK) /* Don't track with kmemcheck */ 85 86#define __GFP_NO_KSWAPD ((__force gfp_t)___GFP_NO_KSWAPD) 87#define __GFP_OTHER_NODE ((__force gfp_t)___GFP_OTHER_NODE) /* On behalf of other node */ 88

이들 플래그는 다음과 같이 조합하여 사용가능하다

ptr = kmalloc(size, __GFP_WAIT | __GFP_IO | __GFP_FS);

이것은 페이지 할당자가 블록되거나, IO를 수행하거나, 파일시스템 연산을 수행할 수 있도록 지정한다. 이러한 방법을 통하여 커널은 할당에 필요한 가용 메모리를 찾기 위한 다양한 선택을 하게 된다.
대부분의 할당에서 동작 조절자가 사용되지만, 사실 대부분의 경우 그것은 다음에 살펴보게 될 유형 플래그를 통해 간접적으로 이뤄진다. 다시 말하자면 메모리를 할당할 때마다 매번 이러한 복잡한 플래그를 찾아보지 않아도 된다는 것이다.

영역 조절자(zone modifier)
영역 조절자는 어떤 메모미영역으로부터 메모리를 할당할 것인가를 지정한다. 일반적인 할당의 경우 어떤 메모리를 사용하더라도 문제가 없지만, 다른 영역에 충분한 여유를 남기기 위하여 커널에서는 ZONE_NORMAL을 사용한다.
ZONE_NORMAL(기본 할당 영역) 외에는 영역이 둘 뿐이므로, 영역 조절자느 역시 두개 뿐이다. 
 플래그 설명 
__GFP_DMA  ZONE_DMA에 할당 
__GFP_HIGHMEM  ZONE_HIGHMEM이나 ZONE_NORMAL에서 할당 

이 두 플래그 중 하나가 사용되면 해당 영역에서 가용한 메모리를 찾게된다. __GFP_DMA 플래그는 ZONE_DMA 영역에서 메모리가 할당되도록 강제한다. 즉 이 플래그는 항상 DMA를 할 수 있는 메모리를 요청한다.
반면 __GFP_HIGHMEM 플래그는 메모리 할당자로 하여금 ZONE_NORMAL 또는 ZONE_HIGHMEM에서 메모리를 할당하도록 한다. 즉, 이 플래그는 상위메모리를 사용할 것이니 일부를 떼어주되, 일반 메모리라도 상관이 없다라고 말하는 것과 같다.
마냥ㄱ, 어떤 플래그도 지정되지 않ㄴ으면 ZONE_DMA나 ZONE_NORMAL에서 메모리를 할당하되 대부분 ZONE_NORMAL에서 메모리를 할당해준다. 즉 플래그를 지정하지 않으면 보통 메모리처럼 동작하면 어떤 것이라도 상관없다고 말하는 것이다.

__GFP_HIGHMEM은 __get_free_pages()나 kmalloc()에 대해서는 지정할수없다.  그 이유는 두 함수가 모두 page구조체가 아닌 논리주소를 리턴하므로, 커널의 가상주소 공간에 매핑되지 않는, 즉 논리적 주소가 없는 메모리를 할당할 수 없기 때문이다.???? 오직 alloc_pages()만이 상위 메모리를 할당할 수 있다. 하지만 대부분의 경우에는 ZONE_NORMAL이면 충분하다.

유형 플래그
 플래그 설명 
 GFP_ATOMIC 이 할당작업은 높은 우선순의를 가지며 휴면할 수 없다. 이 플래그는 인터럽트 핸들러나 보톰하프 수행중, 혹은 스핀락 점유중과 같이 휴면해서는 안되는 상황에서 사용한다. 
 GFP_NOIO 이 할당은 블록될 수 있찌만, 디스크  IO를 일으키지 안흔다. 
 GFP_NOFS 이 할당은 블록될 수 있고, 디스크 IO를 개시할 수도 있지만 파일시스템 연산을 발생시키지는 않늗나. 
 GFP_KERNEL 이것은 일반적인 할당으로 블록될 수 있다. 이 플래그는 휴면상태가 되어도 안전한 프로세스 컨텍스트 코드에서 사용한다. 이 때, 커널은 호출한 쪽에서 요청한 메모리를 할당받기 위해 자기가 해야 하는 것들은 다 한다. 대부분의 경우 이 플래그로 충분하다
GFP_USER  이것은 일반적인 할당으로 블록될 수 있다. 이 플래그는 유저공간 프로세스에 메로리를 할당할 때 사용한다 
 GFP_HIGHUSER 이 할당은 ZONE_HIGHMEM에서 이루어지며 블록될 수 있다. 이 플래그는 유저 공간 프로세스에 메모리를 할당할 떄 사용한다. 
 GFP_DMA 이 할당은 ZONE_DMA에서 이루어진다. 이 플래그는 DMA가능한 메모리를 필요로 하는 디바이스 드라이버에서 사용되며, 보ㄷ통 다른 플래그들과 조합하여 사용한다. 


일반적인 커널 할당에서는 GFP_KERNEL 플래그가 사용된다. 이러한 할당은 보통의 우선순의를 가지며 휴면할 수 있다. 함수가 블록될 수 있으므로, 이 플래그는 안전하게 리스케줄링 할 수 있는(즉 락을 보유하지 않는) 프로세스 컨텍스트에서만 사용할 수 있따. 또한 이 플래그를 사용하면 메모리를 얻는 방법이 제한되지 않으므로 메모리할당이 성공할 가능성이 높아진다.

정반대의 경우로는 GFP_ATOMIC 플래그가 있다. 이 플래그 휴면할 수 없는 메모리 할당을 지정하므로, 호출자에게 넘겨지는 할당된 메모리는 매우 제한적일 수 밖에 없다. 즉, 만약 충분한 크기의 연속된 메모리 조각이 없는 경우 커널은 호출자를 휴면시킬 수 없으므로, 필요한 공간을 확보하기 위해 다른 메모리를 해제하는 등의 작업을 할 수 가 없다. 반면, GFP_KERNEL은 휴면시킬 수 있으므로 페이지를 swap하거나 갱신된 페이지를 디스크로 쓰는 작업을 통하여 공간을 확보할 수 있다.
그러나 ,GFP_ATOMIC은 인터럽트 핸들러나 보콤하프, 태스크릿과 같이 현재 코드를 휴면시킬 수 없는 경우에는 유일한 선택이다.

이 두 플래그의 중간쯤에는 GFP_NOIO와 GFP_NOFS가 있다. 이 플래그를 사용한 할당작업은 블록될 수 있지만, 다른 특정한 작업이 수행되는 것을 방지한다. 즉, GFP_NOIO할당은 어떤 요청이 들어오더라도 디스크IO를 절대 하지 않는다.








 

'OS이야기' 카테고리의 다른 글

vmalloc()  (0) 2011.10.19
kfree()  (1) 2011.10.19
페이지 얻기  (0) 2011.10.18
영역(Zone)  (0) 2011.10.18
메모리 관리  (0) 2011.10.17
:
Posted by НooпeУ


Code Start Code End