linux
zone, free_area, list_head[MIGRATE_TYPES] 관련
easy16
2025. 12. 28. 16:20
Q.free_area는 4K의 승수별로 존재, list_head는 type 별 리스트를 가진다고 이해하면 되나
정확합니다! 당신의 이해가 맞습니다.
정리
free_area[order]의 구조:
free_area[0] (2^0 = 1 page = 4KB)
├─ free_list[MIGRATE_UNMOVABLE] → [page] → [page] → ...
├─ free_list[MIGRATE_MOVABLE] → [page] → [page] → ...
├─ free_list[MIGRATE_RECLAIMABLE]→ [page] → [page] → ...
├─ free_list[MIGRATE_HIGHATOMIC] → [page] → ...
└─ free_list[MIGRATE_CMA] → [page] → ...
free_area[1] (2^1 = 2 pages = 8KB)
├─ free_list[MIGRATE_UNMOVABLE] → [page] → [page] → ...
├─ free_list[MIGRATE_MOVABLE] → [page] → [page] → ...
├─ free_list[MIGRATE_RECLAIMABLE]→ ...
├─ free_list[MIGRATE_HIGHATOMIC] → ...
└─ free_list[MIGRATE_CMA] → ...
free_area[2] (2^2 = 4 pages = 16KB)
├─ free_list[...]
└─ ...
...
free_area[10] (2^10 = 1024 pages = 4MB)
├─ free_list[...]
└─ ...
코드로 보면
struct zone {
struct free_area free_area[MAX_ORDER]; // MAX_ORDER = 11
};
struct free_area {
struct list_head free_list[MIGRATE_TYPES]; // MIGRATE_TYPES = 5
unsigned long nr_free;
};
예시: 8KB 메모리 블록 찾기
// 8KB (2 pages) 크기의 MOVABLE 타입 메모리를 찾고 싶다면
struct list_head *target = &zone->free_area[1].free_list[MIGRATE_MOVABLE];
// ↑ ↑
// order=1 (8KB) type별 리스트
// (2^1 page blocks)
할당/해제 관점
// 8KB 할당 요청이 오면
// → order=1, migratetype=MIGRATE_MOVABLE인 경우
// → zone->free_area[1].free_list[MIGRATE_MOVABLE]에서 페이지 블록 추출
// 8KB 메모리 해제
// → order=1, migratetype=MIGRATE_MOVABLE
// → zone->free_area[1].free_list[MIGRATE_MOVABLE]에 페이지 블록 추가
```
## 마이그레이션 타입 분리의 이점
```
같은 크기(order)여도 타입별로 분리:
free_area[1].free_list[MIGRATE_MOVABLE] → 이동 가능한 8KB 블록들
free_area[1].free_list[MIGRATE_UNMOVABLE] → 이동 불가능한 8KB 블록들
→ 커널이 8KB 메모리를 할당할 때 UNMOVABLE 영역에서만 찾기
→ MOVABLE 영역을 보존 (메모리 압축/조각화 방지)
당신의 이해가 정확합니다!
- ✅ 크기: 4KB의 승수별로 존재 (2^0, 2^1, ... 2^10)
- ✅ 리스트: 각 크기별로 타입에 따른 5개의 리스트 보유
Q. 여기서 MIGRATE_TYPES는 zone의 종류와는다른거야?
네, 완전히 다른 개념입니다!
구분
MIGRATE_TYPES = 페이지의 이동 가능성 속성
#define MIGRATE_UNMOVABLE 0 // 커널이 사용, 이동 불가
#define MIGRATE_MOVABLE 1 // 사용자 프로세스, 이동 가능
#define MIGRATE_RECLAIMABLE 2 // 캐시, 회수 가능
#define MIGRATE_HIGHATOMIC 3 // 원자적 할당 예약
#define MIGRATE_CMA 4 // Contiguous Memory Allocator
Zone = 메모리의 물리적 위치 기준
enum zone_type {
ZONE_DMA, // 0-16MB (DMA 가능)
ZONE_DMA32, // 0-4GB (32bit DMA)
ZONE_NORMAL, // 일반 메모리
ZONE_HIGHMEM, // 고위 메모리 (32bit)
ZONE_MOVABLE, // 이동 가능 영역
// ...
};
struct zone {
struct free_area free_area[MAX_ORDER];
// ...
};
```
## 계층 구조
```
Node (NUMA)
↓
Zone (DMA, NORMAL, HIGHMEM, ...)
↓
free_area[0..10] (크기별: 4KB, 8KB, 16KB, ..., 4MB)
↓
free_list[MIGRATE_TYPES] (타입별: UNMOVABLE, MOVABLE, ...)
↓
[페이지] → [페이지] → [페이지] → ...
구체적 예시
// ZONE_NORMAL에서 8KB 메모리 할당
struct zone *zone = &NODE_DATA(0)->node_zones[ZONE_NORMAL];
// ↑
// 물리적 위치 (Zone)
struct free_area *area = &zone->free_area[1];
// ↑
// 크기 (8KB = 2^1)
struct list_head *list = &area->free_list[MIGRATE_MOVABLE];
// ↑
// 이동 가능성 (Type)
// 실제 페이지 추출
page = list_first_entry_or_null(list, struct page, lru);
```
## 메모리 레이아웃
```
ZONE_DMA (0-16MB)
├─ free_area[0]
│ ├─ free_list[MIGRATE_UNMOVABLE]
│ ├─ free_list[MIGRATE_MOVABLE]
│ └─ ...
├─ free_area[1]
│ ├─ free_list[MIGRATE_UNMOVABLE]
│ └─ ...
└─ ...
ZONE_NORMAL (16MB-4GB)
├─ free_area[0]
│ ├─ free_list[MIGRATE_UNMOVABLE]
│ ├─ free_list[MIGRATE_MOVABLE]
│ └─ ...
├─ free_area[1]
│ ├─ free_list[MIGRATE_UNMOVABLE]
│ └─ ...
└─ ...
ZONE_HIGHMEM (4GB 이상)
├─ free_area[0]
│ ├─ free_list[MIGRATE_UNMOVABLE]
│ └─ ...
└─ ...
커널 코드에서
// mm/page_alloc.c
static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order,
int alloc_flags, const struct alloc_context *ac)
{
struct zoneref *z;
struct zone *zone;
struct page *page = NULL;
// 1. Zone 순회 (DMA → NORMAL → ...)
for_each_zone_zonelist_nodemask(zone, z, ac->zonelist,
ac->highest_zoneidx, ac->nodemask) {
// 2. free_area[order] 선택
// 3. free_list[migratetype] 선택
// 4. 페이지 추출
}
return page;
}
정리
항목기준종류
| Zone | 물리적 위치 | DMA, NORMAL, HIGHMEM, ... |
| free_area[order] | 메모리 크기 | 4KB, 8KB, 16KB, ..., 4MB |
| MIGRATE_TYPES | 페이지 속성/이동성 | UNMOVABLE, MOVABLE, ... |
MIGRATE_TYPES는 Zone과 무관하게, 각 Zone의 모든 free_area에서 타입별로 페이지를 분류하는 것입니다!