리눅스 메모리 관리 1

EEEFFEE·2023년 12월 13일

raspberrypi4-kernel

목록 보기
6/12

23.12.12 최초 작성

1. Linux Memory Management Subsystem

1.1 관련 자료구조

  • mm_struct : 각 가상 주소 공간(프로세스)에 대한 정보를 저장하는 자료구조
  • vm_area_struct : 프로세스의 메모리 영역에 대한 정보 및 속성(file mapped, protection field...) 저장
  • page : 각 page를 관리하는 자료구조

1.2 Address translation layer

  • 각 cpu 구조의 MMU마다 주소 변환 방식이 다름
  • 리눅스는 자신만의 translation scheme을 가짐

2. Page Frame Descriptor

  • struct page : 각 page를 관리하는 자료구조, mem_map(vmemmap) array에 저장되어 관리 됨

// include/linux/mm_types.h
#include <linux/mm.h>

struct page {
	unsigned long flags;		/* Atomic flags, some possibly
					 * updated asynchronously */
	...
    
	/* Usage count. *DO NOT USE DIRECTLY*. See page_ref.h */
	atomic_t _refcount;

	/*
	 * On machines where all RAM is mapped into kernel address space,
	 * we can simply calculate the virtual address. On machines with
	 * highmem some memory is mapped into kernel virtual memory
	 * dynamically, so we need a place to store that address.
	 * Note that this field could be 16 bits on x86 ... ;)
	 *
	 * Architectures with slow multiplication can define
	 * WANT_PAGE_VIRTUAL in asm/page.h
	 */
#if defined(WANT_PAGE_VIRTUAL)
	void *virtual;			/* Kernel virtual address (NULL if
					   not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
}

//
flag : 페이지의 상태를 나타나내는 정보 (dirty page, swapped ...)
struct list_head lru : LRU를 기준으로 
struct address_space *mapping : 페이지가 어디에 속한지 나타냄 
pgoff_t index : 매핑의 오프셋 정보
unsigned long private : private 필드를 나타내는 공간
atomic_t _mapcount : 해당 페이지에 매핑된 Page Table Entry 갯수
atomic_t _refcount : 현재 struct page가 얼마나 많은 프로세스에게 참조되고 있는지 나타냄

2.1 Zone

  • 물리 메모리를 용도에 따라 여러 영역으로 나눈 것
  • Zone의 종류
    • ZONE_DMA : old DMA 장치를 지원하는 물리 메모리 영역(~16MB)
    • ZONE_DMA32 : DMA 장치를 지원하는 최대 32bit 물리 메모리 영역 (~4GB)
    • ZONE_NORMAL : 즉시 할당할 수 있는 물리 메모리 영역으로 대부분의 할당이 발생하는 영역
    • ZONE_HIGHMEM : 특별한 절차를 거쳐 할당할 수 있는 물리 메모리 영역
    • ZONE_MOVABLE : movable한 페이지를 저장하는 물리 메모리 영역
    • ZONE_DEVICE : 비휘발성 페이지를 저장하기 위한 물리 메모리 영역

2.1.1 HIGHMEM

  • 32bit 환경에서 가상 메모리는 4GB로 제한되며 보통 kernel : User = 1 : 3비율로 배분
  • 커널 공간의 가상 메모리는 물리 메모리에 매핑 됨

2.2 Node

  • NUMA 구조에서 같은 특성(접근 속도)을 갖는 메모리
  • struct pglist_data or pg_data_t
    • NUMA 구조에서 개별 노드를 관리하는 자료구조
    • mem_nodes[MAX_NUMNODES] 배열에 저장 됨
    • 각 노드에 kswapd 스레드 존재

//include/linux/mmzones.h
#include <linux/mmzone.h>

typedef struct pglist_data {
    zone_t node_zones[MAX_NR_ZONES];					//해당 노드에 존재하는 zone의 배열
    zonelist_t node_zonelists[GFP_ZONEMASK+1];			//이 노드에서 페이지를 할당할 때 할당을 시도하는 순서
    int nr_zones;										//해당 노드에 존재하는 zone의 개수
    struct page *node_mem_map;							//페이지의 정보를 저장하는 배열
    unsigned long *valid_addr_bitmap;					//유효한 주소의 범위를 나타내는 비트마스크
    struct bootmem_data *bdata;
    unsigned long node_start_paddr;						//노드의 첫 물리 주소
    unsigned long node_start_mapnr;						//전역변수 mem_map 상에서의 오프셋
    unsigned long node_size;							//노드에 존재하는 페이지의 숫자
    int node_id;
    struct pglist_data *node_next;						//다음 노드를 가리키는 포인터
} pg_data_t;

3. 물리 메모리 할당/관리

3.1 Zone Allocator

  • 페이지 할당

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

///
gfp_mask : 
order : 메모리를 얼마나 할당할지 정하는 인자 (2^order)

Return : page descriptor의 시작 주소 값

unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order); 
unsigned long __get_free_page(gfp_t gfp_mask);
unsigned long get_zeroed_page(gfp_t gfp_mask); 							//메모리에 저장된 값을 
																		//0으로 초기화 후 할당 받음
unsigned long __get_dma_pages(gfp_t gfp_mask, int order);				//dma를 위한 메모리 할당

//

Return : 할당한 메모리의 가상 메모리 시작 주소

  • 페이지 해제

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

gfp_mask : 
order : 메모리를 얼마나 해제할지 정하는 인자 (2^order) 반드시 할당할 때 입력한 order와 같은 값을 입력

GFP flags

  • 페이지 할당을 제어하는 플래그

//include/linux/gfp.h
#include <linux/gfp.h>

__GFP_DMA							//DMA에서 페이지 할당
__GFP_HIGHMEM						//HIGHMEM에서 페이지 할당
__GFP_IO							//페이지 할당 시 I/O(write back)를 요청 가능하도록 설정
__GFP_FS							//페이지 할당 시 파일 시스템에 접근 가능
__GFP_DIRECT_RECLAIM 				//할당 시 direct reclaim path를 가능
__GFP_KSWAPD_RECLAIM 				//할당 시 kswapd를 사용할 수 있음
__GFP_HIGH 							//메모리 부족 시 reserved frame 사용할 수 있음
__GFP_ZERO 							//0으로 메모리를 초기화 한 뒤 할당
__GFP_HARDWALL						//해당 메모리를 사용할 cpu를 제한
__GFP_MOVABLE						//할당받은 메모리를 movable로 설정

GFP_ATOMIC					__GFP_HIGH | __GFP_ATOMIC | __GFP_KSWAPD_RECLAIM
__GFP_RECLAIM				__GFP_DIRECT_RECLAIM | __GFP_KSWAPD_RECLAIM
GFP_NOWAIT					__GFP_KSWAPD_RECLAIM
GFP_NOIO					__GFP_RECLAIM
GFP_NOFS 					__GFP_RECLAIM | __GFP_IO
GFP_KERNEL					__GFP_RECLAIM | __GFP_IO | __GFP_FS
GFP_USER 					GFP_KERNEL | __GFP_HARDWALL
GFP_HIGHUSER 				GFP_USER | __GFP_HIGHMEM
GFP_HIGHUSER_MOVABLE		GFP_HIGHUSER | __GFP_MOVABLE

3.2 Zone Watermark

  • Watermark_LOW(Page_low)일 시 kswapd가 깨어나면 비동기적으로 메모리 회수 시작
    • 메모리 할당에 실패할 경우 메모리 할당에 필요한 공간을 확보할 때 까지 실행
    • 과도하게 회수한 경우
  • Watermark_MIN(Page_min)일 시 동기적으로 메모리 회수하고 할당 함
    • node_reclaim() : clean page 해제
    • try_to_free_pages() : Watermark_MIN(Page_min)일 시 dirty page writeback하고 swap out
  • GFP_ATOMIC일 경우 Watermark_MIN(Page_min)을 넘어서 페이지 할당 가능

3.2.1 Watermark 설정

  • _setup_per_zone_wmarks() : Watermark_HIGH/LOW/MIN 설정

#include <mm/page_alloc.c>

_setup_per_zone_wmarks()

LowmemHighmem
WMARK_MINmin_free_kbyte(환경변수) * managed_pages / lowmem_pagezone_pages / 1024
WMARK_LOW1.25 * min1.25 * min
WMAKR_HIGH1.5 * min1.5 * min

3.3 Buddy System

  • Buddy Allocator : 존재하는 block이 할당이 필요한 공간보다 크다면 크기에 맞을 때 까지 이를 반으로 나누어 공간 확보, 해제 시 인접한 다른 free block과 합쳐 더 큰 free block을 만듬

4. 메모리 할당 과정

(강의노트)



5. Memory Mapped I/O

  • 디바이스 I/O 포트를 물리 메모리에 매핑하는 것
  • 디바이스 I/O 포트에 접근 할 때 load/store 인스트럭션 사용

request_mem_region(start, len, name)			//메모리 시작 주소(start)부터 
												//특정 크기(len)만큼 디바이스에 할당 (이름 : name)
release_mem_region(start, len)					//해당 영역 해제

$/proc/iomem									//현재 메모리에 로드된 디바이스 확인 가능

5.1 Mapping MMIO Ports in Kernel(Virtual) Memory


void __iomem *ioremap(phys_addr, size);			//가상 메모리를 물리 메모리
												//(phys_addr ~ phys_addr + size)에 매핑
void iounmap(void* __iomem *addr);				//해제

5.2 Device-managed Allocations

  • 모든 할당 된 디바이스 포트는 해제해줘야 함
  • 디바이스 포트를 위해 자동으로 메모리 공간 해제를 지원하는 함수가 있음

#include <linux/device.h>

devm_kmalloc(dev_t* dev, size, gfp);
devm_kfree(dev_t* dev, ptr);
devm_get_free_pages(dev, gfp, order);
devm_free_pages(dev, addr);
devm_ioremap_resource(dev, resource);


///
dev : 디바이스 포트
size : 디바이스 포트를 할당할 때 입력한 크기
gfp : 플래그

6. Fragmentation Management

  • 기본 page단위 뿐만 아니라 더 큰 단위의 page단위로 메모리를 할당해야 하는 경우 있음
    (kernel : 8KB, PGD : 16KB)

6.1 Movable Page

  • 다른 위치로 이동할 수 있는 page
  • page이동으로 Fragmentation 해소 가능
  • Virtual Memory, Page Cache의 경우 page에 가상 주소가 어디있는지 알아 이동 후 이 값을 수정 가능하지만 Page Table, Kernel Data의 경우 그렇지 않아 이동 불가

6.1.1 Page Block

  • 같은 mobility를 가진 페이지의 모임으로 Fragmentation 예방
  • /proc/pagetypeinfo를 통해 Page Block의 종류 확인 가능
  • 만약 unmovablePage Block에 할당을 할 수 없으면 movalbePage Blockunmovable로 변경
  • Zone의 속성을 통해 확인 가능
    • pageblock_order (2^n) : Page Block의 크기
    • pageblock_nr_pages (2^n) : page의 갯수

6.1.2 Compaction

  • Fragmentation이 심할 경우 조각난 페이지를 합치는 방법
  • page할당이 실패했을 경우 실행
  • echo 1 > /proc/sys/vm/compact_memory를 통해 실행 가능

6.2 Slab Cache (Object Caching Memory Allocator)

  • 페이지를 더 작은 단위로 나눠 메모리를 할당하는 방법 (Internal Fragment 방지)
  • 페이지를 요청해 미리 일정 크기로 자름 (caching) 이 때 나눠진 크기의 메모리를 object, object의 모임을 object cache 라고 함
  • 해당 크기에 알맞는 크기의 메모리 할당 요청이 올 때 object를 할당하고 이 메모리가 반환될 시 object cache로 반환
  • 공간 부족 시 object cache에 추가로 메모리 할당 받음
  • /proc/slabinfo에서 object cache확인 가능

6.

(강의노트)

7. ARMv8 메모리 관리

  • TTBR0

    • 사용자 공간의 Page Table
    • context switching 시 내용 변경
  • TTBR1 :

    • 커널 공간의 Page Table
    • context switching 시 내용 변경 안됨

7.1 ARMv8 관련 가상메모리 구조

  • Documentation/arm64/memory.rst : 가상 메모리 변환 테이블 확인
    • 4단계의 Multi-Level Page Table
Translation table lookup with 4KB pages::

  +--------+--------+--------+--------+--------+--------+--------+--------+
  |63    56|55    48|47    40|39    32|31    24|23    16|15     8|7      0|
  +--------+--------+--------+--------+--------+--------+--------+--------+
   |                 |         |         |         |         |
   |                 |         |         |         |         v
   |                 |         |         |         |   [11:0]  in-page offset
   |                 |         |         |         +-> [20:12] L3 index
   |                 |         |         +-----------> [29:21] L2 index
   |                 |         +---------------------> [38:30] L1 index
   |                 +-------------------------------> [47:39] L0 index
   +-------------------------------------------------> [63] TTBR0/1
  • arch/arm64/include/asm/pgtable-hwdef.h : 페이지 테이블 구조 확인
/*
 * Size mapped by an entry at level n ( 0 <= n <= 3)
 * We map (PAGE_SHIFT - 3) at all translation levels and PAGE_SHIFT bits
 * in the final page. The maximum number of translation levels supported by
 * the architecture is 4. Hence, starting at level n, we have further
 * ((4 - n) - 1) levels of translation excluding the offset within the page.
 * So, the total number of bits mapped by an entry at level n is :
 *
 *  ((4 - n) - 1) * (PAGE_SHIFT - 3) + PAGE_SHIFT
 *
 * Rearranging it a bit we get :
 *   (4 - n) * (PAGE_SHIFT - 3) + 3
 */

7.2 Page Table 확인

apt install linux-source libelf-dev libssl-dev dwarves
tar jxvf /usr/src/linux-source-5.15.0.tar.bz2
// 커널 소스 다운로드

cd linux-source-5.15.0
make localmodconfig
make menuconfig
//(kernel hacking -> memory debugging -> export kernel page table)

make –j5
make modules_install && make install
//컴파일 완료 후 재부팅

mount –t debugfs
cat /sys/kernel/debug/kernel_page_tables
//페이지 테이블 확인

0개의 댓글