Linux kernel Memory management - 1

2022. 5. 7. 16:18Linux

    목차
반응형

Memory management

word ? page ?
 
커널은 물리적 페이지를 메모리 관리의 기본 단위로 사용
processor memory에 접근 시 byte word 단위로 접근, MMU page 단위로 처리함
 
    * virtual memory 관점에서는 page가 유의미한 최소 단위임

 

page 크기

    대부분의 32 bit architecture 4KB

    64bitarchitecture의 경우 8KB

 

 

page 구조체

    struct page 구조체를 사용해모든 물리적 page를 표현

    <linux/mm_types.h>에정의

 

struct page {
        unsigned long flags;
        atomic_t _count;
        atomic_t _mapcount;
        ...
        struct list_head lru;
        void *virtual;
   };

 

    _count항목: page의 사용 횟수, 참조 횟수

    virtual 항목: page의 가상 주소를 나타냄

        가상 주소 상의 page 주소

        high mem처럼 커널 주소공간에 고정되어 할당되지 않은 경우 NULL 값임

            . 커널 address space에 고정되어 있어야 값을 지님

             (이 경우 필요 시 동적 page설정)

 

    page 구조체는 가상 page가 아닌 물리적 page를 나타낸다.

        Page 구조체를 사용해서physical page-frame의 사용 여부를 판단할 수 있음

        Process (만약 해당 page refcount -1이면) 사용 가능함 (multiple map 가능)

        -> , 일시적인 내용임 (swap 등이 발생 가능)

        -> , data가 같은 page 구조체에 들어있을 보장이 없음

    page 소유자는

1)    user space process

2)    동적 할당된 kernel data

3)    정적 kernel code

4)    page cache 등이 가능

       

    page 구조체의 크기가 40Byte의 경우

        4GB의물리적 메모리에 대해 약 이 구조체가 20MB 를 사용

 

 

zone (구역)

    page가관리할 memory device (node) HW적 차이로인해 서로 다르게 취급하기 위함

        zone으로구분 - 비슷한 속성 별 page들의 모음

HW적 단점 극복 위함   

1)    일부 HW 장치는 특정 memory 주소로만 DMA 사용 가능 (예전 x86 - 하위 16 MB)

2)    일부 architecture에서는 가상적으로접근 가능한 것보다 많은 물리적 memory로 접근 가능 (이경우 일부는 kernel 주소 공간에 상주 할 수 없음)

 

4가지 zone (Linux는 필요할때 할당할 수 있도록 system page 논리적으로 구역별로 관리)

        . ZONE_DMA : DMA 를 수행할 수 있는page가 존재 (memory mapped 가 아님)

        . ZONE_DMA_32 : ZONE_DMA와 동일하나 차이는 32 비트 장치들만 접근 가능

        . ZONE_NORMAL : 정상적인, 통상적으로할당되는 page들 존재 영역

        . ZONE_HIGHMEM : 커널 주소 공간에 상주하지 않는 page (상위 메모리) 가 존재

       <linux/mmzone.h> 참고

 

일부 architecture (ARM의 경우)는모든 memory 영역에서 DMA를 수행하는데 문제가 없음

        -> memory mapped I/O 경우

x86의 경우 ISA 장치는 물리적memory의 앞부분 16MB만 접근할 수 있음

(전체 32비트 주소 공간에 DMA를수행할 수 없음)

 

x86 system의 경우 ZONE_HIGHMEM구역은 물리적으로 896MB를 넘어간 모든 memory가해당

다른 architecture 경우 모든memory에 직접 접근이 가능하므로 ZONE_HIGHMEM은 비어있음

        cf.

           ZONE_HIGHMEM 구역에 속한 memory high memory라고 함

            나머지는 low memory

 

일반적인 page ZONE_DMA, 혹은 ZONE_NORMAL 구역 어느 쪽에서도 할당할 수 있음

되도록이면 일반적인 할당 작업은 일반 구역에서 처리 함

x86-64에는ZONE_HIGHMEM 구역이 없으며 ZONE_DMAZONE_NORMAL 구역에 모든 물리적 메모리가 들어감

 

zone 구조체 (논리적 memory 구역을 구분하고 관리하는 구조체)

struct zone {
        unsigned long        watermark[NR_WMARK];       
        unsigned long        lowmem_reserve[MAX_NR_ZONES];
        struct per_cpu_pageset PAGESET[NR_CPUS];
        spinlopck_t          lock;
        struct free_area    free_area[MAX_ORDER];
        spinlock_t           lru_lock;
        struct zone_lru {
            struct list_head list;
            ...
        };
        ...
        const char *name;       <- "DMA" or "NORMAL" or "HighMem"
    };

 

system에는 세 가지 구역밖에 없으므로 세 개의 구조체만 존재 (2.6 기준)

watermark 배열에는 해당 구역의 메모리 사용량에 대한 최소, 하위, 상위 수치 정보가 존재

mm/page_alloc.c file에서 name 항목을 초기화

 

 

page 가져오기

    Kernel 내부에서memory 할당을 위해 구현된 kernel interface

   <linux/gfp.h>

   

struct page * alloc_pages(gfp_t gfp_mask, unsigned int order)

2order(1<<order) 개수만큼 연속된 물리적 page를할당하고

        첫번째 페이지의 page 구조체 pointer를 반환

 

void * page_address(struct page *page)

        물리적 page에 현재 담겨있는 논리적 주소 pointer를 반환

 

unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)

        struct page 구조체가 필요하지 않은 경우 사용

        요청한 첫 번째 page의 논리적주소를 반환 (나머지는 alloc_pages와 동일)

 

struct page * alloc_page(gfp_t gfp_mask)
unsigned long __get_free_page(gfp_t gfp_mask)

       한 개의 page를 할당/해제

 

0으로 채워진 page 가져오기

    unsigned long get_zeroed_page(unsigned int gfp_mask)

1)    __get_free_page와 동일하나 page 내용을 0으로 채움

2)    system 보안을 유지하려고 사용자 공간으로반환하는 page의 모든 data 0으로 채우거나 그에 준하는 정리작업을 해야 함

 

저수준 page 할당 함수들

    alloc_page(gfp_mask) Allocates a single page and returns a pointer to its
alloc_pages(gfp_mask, order) Allocates 2order pages and returns a pointer to the first page’s page structure
__get_free_page(gfp_mask) Allocates a single page and returns a pointer to its logical address
__get_free_pages(gfp_mask) Allocates 2order pages and returns a pointer to the first page’s logical address
get_zeroed_page(gfp_mask) Allocates a single page, zero its contents and returns a pointer to its logical address

 

페이지 반환

    할당된 page를 해제하는 함수들

void __free_pages(struct page *page, unsigned int order)
void free_pages(unsigned long addr, unsigned int order)
void free_page(unsigned long addr)

 

Ex.

// alloc. 8 pages       
unsigned long page;
page = __get_free_pages(GFP_KERNEL, 3);
if (!page) {
/* insufficient memory: you must handle this error! */
return ?ENOMEM;
}

 

// free the 8 pages
free_pages(page, 3);

 

GFP_KERNEL 인자

gfp_mask flag 중 하나임   

    저수준 page 함수들은 물리적으로 연속된 page 단위의 memory가 필요한 경우,

    특히정확히 한두 개의 page가 필요한 경우에 유용함   

    더일반적인 byte 단위 memory 할당을 위해 kernel kmalloc 함수를 제공   

   

kmalloc()

    flags 인자가추가됐다는 점을 제외하고는 malloc과 유사

    page가통째로 필요한 경우에는 앞서 살펴본 interface를 사용하는 편이 좋음

(e.g., alloc_pages())

   

<linux/slab.h>
void * kmalloc(size_t size, gfp_t flags)

       kmalloc으로 할당된 메모리 영역은 물리적으로 연속된 영역임

   

p = kmalloc(sizeof(struct dog), GFP_KERNEL);
if (!p)
/* handle error ... */

 

gfp_mask Flags

    gfp는앞서 살펴본 할당 함수 중의 하나인 __get_free_pages()를 의미   

    flag동작 지정자, 구역 지정자, 형식 세 가지로분류

 

    예를들면,

        1. 동작 지정자: 인터럽트 핸들러는 (re-scheduling이 불가능

            커널에게 memory  할당 작업 중 휴면 상태로전환하지 말라고 알려줘야 함)

        2. 구역 지정자: 어느 곳의 memory를 할당할지 지정

        3. 형식 지정자: 동작 지정자와구역 지정자가 조합된 것

 

Action Modifiers (동작 지정자)

   <linux/gfp.h>   <-- GFP (Get Free Pages)

   <linux/slab.h>  <-- 에서위 동작 지정자가 들어있는 gfp.h를 포함   

    현실에서는 나중에 살펴볼 형식 flag만 사용하는 경우가 보통임

__GFP_WAIT The allocator can sleep.
__GFP_HIGH The allocator can access emergency pools
__GFP_IO The allocator can start disk I/O
__GFP_FS The allocator can start filesystem I/O
__GFP_NOWARN The allocator does not print failure warnings
__GFP_REPEAT The allocator repeats the allocation if it fails, but the allocation can potentially fail
__GFP_NOFAIL The allocator indefinitely repeats the allocation. The allocation cannot fail
__GFP_NORETRY The allocator never retries if the allocation fails
__GFP_NOMEMALLOC The allocator does not fall back on reserves
__GFP_HARDWALL The allocator enforces “hardwall” cpuset boundaries
__GFP_RECLAIMABLE The allocator marks the pages reclaimable.
__GFP_COMP The allocator adds compound page metadata (used internally by the hugetlb code)
__GFP_COLD The allocator should use cache cold pages

 

    다음과같이 조합해서 사용 가능

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

       할당 작업이 중단 가능하며, 필요할 경우 입출력이나 file system을 사용할 수 있음

 

Zone Modifiers

__GFP_DMA Allocates only from ZONE_DMA
__GFP_DMA32 Allocates only from ZONE_DMA32
__GFP_HIGHMEM Allocates from ZONE_HIGHMEM or ZONE_NORMAL

 

    보통 ZONE_NORMAL을 우선 사용함   

 

__get_free_pages()kmalloc() 함수에서는 __GFP_HIGHMEM을 지정할 수 없음

1)    이 두 함수는 page 구조체가 아니라 논리적주소를 반환

 page struct가 아니라주소를 반환하나?

a.    현재 kernel의 가상 memory 공간에 올라와 있지 않아

b.    논리적 주소가 없는 memory를 할당하는 상황이벌어질 수 있기 때문

è  Virtual address space mapping의 보장을 위함

   

    alloc_pages()  함수만 상위 memory를할당할 수 있음 (kmalloc은 불가능)   

    그러나대부분의 경우 ZONE_NORMAL이면 충분하기에 구역 지정자를 사용하지 않음

 

Type Flags

    미리정의된 flag set

   

    동작지정자와 구역 지정자를 정해둔 것임

 

GFP_ATOMIC 우선순위가 높고, 휴면 상태로 전환하면 안 되는 할당 작업. ISR, 후반부 처리 작업
spinlock 사용 중인 경우와 같이 휴면 상태로 전환할 수 없는 경우에 이 flag를 사용
GFP_NOWAIT 비상용 memory 영역을 사용할 수 없다는 점만 제외하면 GFP_ATOMIC과 유사 (상대적으로 fail 가능성 높음)
GFP_NOIO This allocation can block, but must not initiate disk I/O. This is the flag to use in block I/O code when you cannot cause more disk I/O, which might lead to some unpleasant recursion
GFP_NOFS This allocation can block and can initiate disk I/O, if it must, but it will not initiate a filesystem operation. This is the flag to use in filesystem code when you cannot start another filesystem operation
GFP_KERNEL 중단 가능한 일반적인 할당 작업. 안전하게 휴면 상태로 전환할 수 있는 process context에서 사용
커널은 요청한 memory를 할당하는 데 필요한 모든 일을 시도. 기본값으로 사용하는 flag
GFP_USER This is a normal allocation and might block. This flag is used to allocate memory for user-space processes
GFP_HIGHUSER This is an allocation from ZONE_HIGHMEM and might block. This flag is used to allocate memory for user-space processes
GFP_DMA ZONE_DMA 구역의 메모리를 할당함. 보통 DMA 가능한 memory가 필요한 device driver에서 이 flag를 다른 flag와 조합해서 사용

 

 

/* This equals 0, but use constants in case they ever change */
#define GFP_NOWAIT  (GFP_ATOMIC & ~__GFP_HIGH)
/* GFP_ATOMIC means both !wait (__GFP_WAIT not set) and use emergency pool */
#define GFP_ATOMIC  (__GFP_HIGH)
#define GFP_NOIO    (__GFP_WAIT)
#define GFP_NOFS    (__GFP_WAIT | __GFP_IO)
#define GFP_KERNEL  (__GFP_WAIT | __GFP_IO | __GFP_FS)
#define GFP_TEMPORARY      (__GFP_WAIT | __GFP_IO | __GFP_FS | \
                     __GFP_RECLAIMABLE)
#define GFP_USER    (__GFP_WAIT | __GFP_IO | __GFP_FS |
 __GFP_HARDWALL)
#define GFP_HIGHUSER       (__GFP_WAIT | __GFP_IO | __GFP_FS |
 __GFP_HARDWALL | __GFP_HIGHMEM)
...
#ifdef CONFIG_NUMA
#define GFP_THISNODE       (__GFP_THISNODE | __GFP_NOWARN |
 __GFP_NORETRY)
#else
#define GFP_THISNODE       ((__force gfp_t)0)
#endif
...
#define GFP_DMA            __GFP_DMA
 
/* 4GB DMA on some platforms */
#define GFP_DMA32   __GFP_DMA32

 

GFP_KERNEL

1)    커널의 대다수 할당 작업은 GFP_KERNEL flag를사용한다.

2)    휴면할 수 있는 할당 방식 -> 메모리할당 성공 확률이 상당히 높다.

3)    휴면 불능 시 원하는 크기의 연속된 memory가없다면 커널은 memory를 확보하기 힘듦

4)    하지만 GFP_KERNEL 플래그를 사용하는할당 작업의 경우에는 메모리를 요청한 process를 휴면시키고 비활성화된 page를 디스크 swap 공간으로 옮기고, 페이지의 변경된 내용을 디스크에 저장하는 등의 작업을 실행

 

GFP_ATOMIC

GFP_ATOMIC의 경우 이런 작업을 할 수 없어서 (memory가충분하지 않은 상황에서는) GFP_KERNEL에 비해서 실패할 확률이 높음

    그렇지만 ISR, tasklet 같이 휴면 상태로 전환할 수 없는 코드에서는GFP_ATOMIC flag만 가능

 

GFP_NOIO

       요청을 처리하는 동안 disk 입출력 작업을 할 수 없음

GFP_NOFS

       disk 입출력은 할 수 있으나, filesystem 입출력 작업은 할 수없음

 

   GFP_NOIO, GFP_NOFS은 각각 특정 저수준 block 입출력 system file system code에서 사용

   

    할당자를 호출하는코드에서 또 다른 할당자를 실행하지 않는 것을 확실히 보장하지 않으면, .할당 과정에서 데드락이 발생할수 있음

    그래서 NOFS와 같은 flag를 사용해서 할당해야 하는 상황이 존재함

 

GFP_DMA

       ZONE_DMA 구역에서 할당 작업을 처리해야 할 경우 사용

       device driver에서 사용

       

대부분의 코드에서는 GFP_KERNEL 혹은 GFP_ATOMIC 둘 중 하나를 사용

 

적절한 flag사용

Process context, can sleep Use GFP_KERNEL
Process context, cannot sleep Use GFP_ATOMIC, or perform your allocations with GFP_KERNEL at an earlier or later point when you can sleep
Interrupt handler Use GFP_ATOMIC
Softirq Use GFP_ATOMIC
Tasklet Use GFP_ATOMIC
Need DMA-able memory, can sleep (GFP_DMA | GFP_KERNEL)
Need DMA-able memory, cannot sleep (GFP_DMA | GFP_ATOMIC), or perform your allocation at an earlier point when you can sleep

 

 kfree()

<linux/slab.h>
void kfree(const void *ptr)

 

vmalloc()

1)    물리적으로 연속될 필요 없이 가상적으로 연속된memory 영역을 할당한다는 점을 제외하면 kmalloc()과 유사한 방식으로 동작

2)    vmalloc()함수가 반환하는 page process의 가상 주소 공간에서 연속된 공간이지만,    kmalloc 함수는 할당된 page 물리적으로도 (그리고 가상적으로도) 연속된 공간이라는 것을 보장

   

많은 architecture에서hardware 장치는 memory 관리 장치가 없는 곳에 살고 있어서, 가상 주소를 처리할 수 없다. (no IOMMU)

   

특정한 경우에만 물리적으로 연속된 memory가 필요함에도 불구하고

kernel에서는 메모리를 할당할 때 대부분 vmalloc이 아닌 kmalloc을 사용 이는 주로 성능 때문

 

1)    vmalloc은 상당량의 page table 항목 조정 작업을 수행

2)    또한 계속 물리적 page가 변경될 수있기에 TLB 를 휠씬 많이 사용

3)    이런 문제로 vmalloc 함수는 절대적으로필요한 경우에만 사용

       보통 큰 영역의 memory를 할당하는 경우가 이에 해당

 

mm/vmalloc.c
void * vmalloc(unsigned long size)

 

휴면 상태로 전환이 가능

           ISR interrupt context에서 사용 불가

      

void vfree(const void *addr)

 

 

 

 

반응형