23.12.12 최초 작성
mm_struct : 각 가상 주소 공간(프로세스)에 대한 정보를 저장하는 자료구조vm_area_struct : 프로세스의 메모리 영역에 대한 정보 및 속성(file mapped, protection field...) 저장page : 각 page를 관리하는 자료구조MMU마다 주소 변환 방식이 다름translation scheme을 가짐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가 얼마나 많은 프로세스에게 참조되고 있는지 나타냄
Zone의 종류ZONE_DMA : old DMA 장치를 지원하는 물리 메모리 영역(~16MB)ZONE_DMA32 : DMA 장치를 지원하는 최대 32bit 물리 메모리 영역 (~4GB) ZONE_NORMAL : 즉시 할당할 수 있는 물리 메모리 영역으로 대부분의 할당이 발생하는 영역ZONE_HIGHMEM : 특별한 절차를 거쳐 할당할 수 있는 물리 메모리 영역ZONE_MOVABLE : movable한 페이지를 저장하는 물리 메모리 영역ZONE_DEVICE : 비휘발성 페이지를 저장하기 위한 물리 메모리 영역kernel : User = 1 : 3비율로 배분NUMA 구조에서 같은 특성(접근 속도)을 갖는 메모리struct pglist_data or pg_data_tNUMA 구조에서 개별 노드를 관리하는 자료구조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;
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

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 outGFP_ATOMIC일 경우 Watermark_MIN(Page_min)을 넘어서 페이지 할당 가능_setup_per_zone_wmarks() : Watermark_HIGH/LOW/MIN 설정#include <mm/page_alloc.c>
_setup_per_zone_wmarks()
| Lowmem | Highmem | |
|---|---|---|
| WMARK_MIN | min_free_kbyte(환경변수) * managed_pages / lowmem_page | zone_pages / 1024 |
| WMARK_LOW | 1.25 * min | 1.25 * min |
| WMAKR_HIGH | 1.5 * min | 1.5 * min |
Buddy Allocator : 존재하는 block이 할당이 필요한 공간보다 크다면 크기에 맞을 때 까지 이를 반으로 나누어 공간 확보, 해제 시 인접한 다른 free block과 합쳐 더 큰 free block을 만듬(강의노트)
load/store 인스트럭션 사용request_mem_region(start, len, name) //메모리 시작 주소(start)부터
//특정 크기(len)만큼 디바이스에 할당 (이름 : name)
release_mem_region(start, len) //해당 영역 해제
$/proc/iomem //현재 메모리에 로드된 디바이스 확인 가능
void __iomem *ioremap(phys_addr, size); //가상 메모리를 물리 메모리
//(phys_addr ~ phys_addr + size)에 매핑
void iounmap(void* __iomem *addr); //해제
#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 : 플래그
page단위 뿐만 아니라 더 큰 단위의 page단위로 메모리를 할당해야 하는 경우 있음kernel : 8KB, PGD : 16KB)pagepage이동으로 Fragmentation 해소 가능Virtual Memory, Page Cache의 경우 page에 가상 주소가 어디있는지 알아 이동 후 이 값을 수정 가능하지만 Page Table, Kernel Data의 경우 그렇지 않아 이동 불가mobility를 가진 페이지의 모임으로 Fragmentation 예방/proc/pagetypeinfo를 통해 Page Block의 종류 확인 가능unmovable한 Page Block에 할당을 할 수 없으면 movalbe한 Page Block을 unmovable로 변경Zone의 속성을 통해 확인 가능pageblock_order (2^n) : Page Block의 크기pageblock_nr_pages (2^n) : page의 갯수Fragmentation이 심할 경우 조각난 페이지를 합치는 방법page할당이 실패했을 경우 실행echo 1 > /proc/sys/vm/compact_memory를 통해 실행 가능Internal Fragment 방지)caching) 이 때 나눠진 크기의 메모리를 object, object의 모임을 object cache 라고 함 object를 할당하고 이 메모리가 반환될 시 object cache로 반환object cache에 추가로 메모리 할당 받음/proc/slabinfo에서 object cache확인 가능(강의노트)
TTBR0
Page Tablecontext switching 시 내용 변경TTBR1 :
Page Tablecontext switching 시 내용 변경 안됨Documentation/arm64/memory.rst : 가상 메모리 변환 테이블 확인Multi-Level Page TableTranslation 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
*/
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
//페이지 테이블 확인