출처: virtual linux manager by Mel Gorman
memory 는 bank로 나뉘어짐
bank는 node라고 불리며,
node는 struct pglist_data로 표현됨.(UMA일때도 마찬가지)
pglist_data는 pg_data_t로 참조된다.
시스템의 모든 node는 NULL로 끝나는 pgdata_list 이라는 리스트로 관리된다.
그리고 각 node는 pg_data_t->node_next로 다른 node와 연결된다.
UMA의 경우 contig_page_data라는 하나의 pg_data_t 구조체를 사용한다.(노드가 하나 이니까)
각 node는 'zone' 이라 불리는 여러개의 블록으로 나뉜다.
zone은 struct zone_struct 이라는 구조체로 정의되며, zone_t로 사용자정의된다.
zone의 종류에는,
ZONE_DMA
ZONE_NORMAL
ZONE_HIGHMEM
(ZONE_MOVEABLE)
이 있다.
신경써야 할 부분은 ZONE_NORMAL이다. ZONE_HIGHMEM은 ZONE_NORMAL의 상위 위쪽 부분이다.
시스템의 메모리는 page frame이라는 고정된 chunk들로 이루어져 있다.
각각의 physical page frame들은 struct page라는 구조체로 표현되며,
이 모든 구조체들은 mem_map이라는 배열에 저장된다.
(mem_map은 보통 ZONE_NORMAL 시작부분이나 지정된 커널 로딩 영역 뒤에 저장된다)
2.1 Nodes
리눅스는 page를 할당할 때, node-local alloocation policy를 사용해서
현재 실행 중인 CPU에서 가장 가까운 node의 메모리를 할당한다.
pg_data_t, 즉 pglist_data는 다음가 같이 정의되어 있다.
node_zones | ZONE_HIGHMEM, ZONE_NORMAL_ZNOE_DMA가 있다. |
node_zonelists | zone들이 할당되는 순서를 가지고 있다. free_area_init_core()에 호출 된 mm/page_alloc.c의 build_zonelists()가 순서를 정한다. |
nr_zones | zones의 갯수 |
valid_addr_bitmap | 메모리 node에 있는 "holes" 에 대한 비트맵 |
bdata | boot memory allocator |
node_start_paddr | node의 시작 주소(physical) Page Frame Number(PFN)에 기록하는게 좋다. PFN은 physicla memory의 index이며, page-sized unit 단위로 세어진다. PFN 은 (page_phys_addr >> PAGE_SHIFT) 로 손쉽게 정의될 수 있다. |
node_start_mapnr | 전역 mem_map안의 page offset. free_area_init_core()에서 mem_map과 지역적 mem_map사이의 페이지 개수로 계산됨 |
node_size | zone안의 총 page 수 |
node_id | node ID (NID). 0부터 시작함 |
node_next | 위에서 말한 node list의 다음 node를 가리킴 |
앞서 말했듯이, 시스템의 모든 node들은 pgdat_list라는 리스트에서 관리되며, init_bootmem_core()에 의해 이 리스트에 추가된다.
2.2 Zones
각 zone들은 struct zone_struct으로 구성된다. (linux/mmzone.h 참조)
lock | zone을 보호하는 spinlock |
free_pages | zone에 존재하는 free page |
pages_min, page_low, pages_high |
|
zone_pgdata | pg_data_t 를 가리킨다. |
zone_mem_map | 전역 mem_map |
zone_start_paddr | node_start_paddr와 유사 |
zone_start_mapnr | node_start_mapnr와 유사 |
name | zone 이름, "DMA", "NORMAL", "HighMem" |
size | pages단위로 표현된 zone의 사이즈 |
2.2.1 Zone Watermarks
시스템에 남아있는 메모리가 적을 경우, kswapd라는 pageout 데몬이 실행되어 page들을 free하기 시작한다. 만약 메모리를 많이 회수해야 한다면, kswapd 데몬은 동기적으로 메모리를 free 한다.(이 방식은 direct-reclaim이라고 불리기도 한다.)
각 zoned은 세가지의 watermark를 가지고 있는데, 이들은 각각 다음과 같다.
pages_low |
free page의 수가 pages_low에 이르면, kswapd가 buddy allocator에 의해 깨어나서 page들을 free한다 |
pages_min |
pages_min에 이르면, buddy allocator는 kswapd를 동기화적으로 돌린다. |
pages_high |
page_high에 이르면 kswapd는 다시 sleep으로 돌아간다. |
pages_min은 free_area_init_core()에서 메모리 초기화 과정에서 page 단위로 표현된 zone의 크기로 표현되어 계산된다.
2.2.2 Calculating the size of Zones
각 zone의 크기는 setup_memory()에서 계산된다.
PFN은 pages단위로 세어진 실제 메모리 맵(physical memory map)안의 오프셋이다.
min_low_pfn은 첫번째 PFN이며, _end 다음의 첫번째 페이지에 위치한다. min_low_pfn은 나중에 쓰일 boot memory allocator를 위해 mm/bootmem.c 에 file scope 변수로 지정되어 있다.
unsigned long min_low_pfn;
unsigned long max_low_pfn;
unsigned long max_pfn;
마지막 페이지 프레임인 max_pfn 은 아키텍쳐 의존적으로 계산된다. x86의 경우 find_max_pfn()으로 e820 map을 통해 가장 높은 값을 갖는 페이지 프레임을 읽는다. (e820은 BIOS를 통해 얻어지는 테이블이며, 어떤 physicla memory가 있는지, 예약되어 있는지등을 알려준다)
max_low_pfn은 (x86의 경우) find_max_low_pfn()을 통해 계산되며, 이는 ZONE_NORMAL의 끝을 표시한다.
ZONE | |
_end | |
min_low_pfn | first page |
pages ... | |
max_low_pfn | end of ZONE_NORMAL <- kernel/userspace split point |
2.2.3 Zone Wait Queue Table
(생략)
2.3 Zone Initialization
zone 들은 kernel page table 이 paginig_init()에 의해 완전히 세팅되고 나면 초기화 된다. 페이지 테이블 초기화에 대해서는 3.6을 참조하자.
아키텍쳐마다 방식에는 다르겠지만, 기본적으로 UMA 일 경우, free_area_init()에 보낼 매개변수를, NUMA일 경우, free_area_init_node()에 보낼 매게변수를 결정한다. 이 매게변수들은 다음과 같다.
nid | 초기화 될 zone을 가지고 있는 NODE의 NODE id |
pgdat | 초기화되는 node의 pg_data_t이며, UMA의 경우 contig_page_data이다. |
pmap | free_area_init_core()에 의해 지정되며, local mem_map의 시작부분을 가리킨다. NUMA는 mem_map을 PAGE_OFFSET에 시작하는 가상 배열로 생각하기 때문에 무시하며, UMA의 경우 pmap은 global mem_map을 가리킨다. |
zone_sizes | 각 zone의 크기를 담고 있는 배열 |
zone_start_paddr | 첫번째 zone의 시작 실제 주소(starting physical address) |
zone_holes | zones에 있는 메모리 홀의 총 사이즈를 담고 있는 배열 |
free_area_init_core()는 각 zone_t의 내용을 연관성이 있는 정보로 채우는 역활 및 mem_map 배열 할당을 한다. 현 상황에서는 어떤 페이지가 free인지는 알 수 없으며, boot memory allocator의 작업이 끝날 때 까지는 알 수 없다.
2.4 Initializing mem_map
mem_map은 시스템 초기화 과정에서 생성된다.
NUMA의 경우, global mem_map이 PAGE_OFFSET에서 시작하는 가상 배열로 취급되며, 각 node 마다 free_area_init_node()를 호출하여 이 가상 배열을 할당한다.
UMA의 경우, free_area_init()이 contig_page_data를 node로, global mem_map을 local mem_map으로 동일하게 취급한다.
locla mem_map은 free_area_init_core()에 의해 할당되며, 이를 위한 배열은 boot memory allocator가 alloc_bootmem_node()를 호출하여 할당한다. UMA의 경우, 이렇게 할당된 메모리는, 위에서 말한 거 처럼, global mem_map이 되나, NUMA의 경우 조금 다르다. NUMA는 local mem_map을 각 node의 고유한 메모리에서 할당하며, global mem_map의 경우 따로 할당되지는 않으나, (가상 배열로 취급되는)PAGE_OFFSET으로 설정된다. local map은 pg_data_t->node_mem_map에 저장된다.
node에 존재하는 각 zone들은 가상 mem_map의 주소를 zone_t->zone_mem_map에 저장한다. 이 외의 작업에서는 mem_map을 실제 배열로 취급한다.
2.5 Pages
모든 시스템의 physical page frame은 struct page를 가지고 있다.
'리눅스 > 커널' 카테고리의 다른 글
__initcall(), module_init() (0) | 2012.07.01 |
---|---|
리눅스 커널 분석 시 아키텍쳐별 tag 및 cscope생성 (0) | 2012.04.16 |
GCC, typeof (0) | 2012.02.23 |
Documentation/arm/booting (0) | 2011.09.25 |
Linux 3.0 Kernel (0) | 2011.07.31 |