72. 메모리 관리 내부 (mm/internal.h)
개요
mm/internal.h는 Linux 커널 메모리 관리 서브시스템의 내부 정의를 담고 있는 핵심 헤더 파일입니다. 이 파일은 외부에 공개되지 않는 내부 구조체, 유틸리티 함수, 매크로를 정의하여 MM 서브시스템 간의 통신과 데이터 교환을 담당합니다.
/* internal.h: mm/ internal definitions - 1773줄 */
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/pagewalk.h>
#include <linux/rmap.h>
#include <linux/swap.h>
#include "vma.h"
이 파일은 page_alloc.c, vmscan.c, compaction.c, rmap.c, filemap.c, mremap.c 등 MM 서브시스템 전반에서 사용되며, 공개 API와 내부 구현 사이의 다리 역할을 합니다.
빠른 점검 명령
# internal.h 줄 수 확인
wc -l ~/Chamber_linux/linux-7.0-source/mm/internal.h
# 포함된 주요 구조체 목록
grep -n "^struct" ~/Chamber_linux/linux-7.0-source/mm/internal.h
# GFP 플래그 마스크 정의 확인
grep -n "GFP_" ~/Chamber_linux/linux-7.0-source/mm/internal.h | head -20
# 할당 관련 상수 확인
grep -n "ALLOC_" ~/Chamber_linux/linux-7.0-source/mm/internal.h
# 함수 선언 확인
grep -n "^void\|^int\|^static inline\|^extern" ~/Chamber_linux/linux-7.0-source/mm/internal.h | head -30
# CONFIG 옵션 확인
grep -n "CONFIG_" ~/Chamber_linux/linux-7.0-source/mm/internal.h | sort -u | head -20
# 내부 매크로 확인
grep -n "^#define" ~/Chamber_linux/linux-7.0-source/mm/internal.h | head -30
# vma 관련 유틸리티 확인
grep -n "vma" ~/Chamber_linux/linux-7.0-source/mm/internal.h | head -15
# swap 관련 함수 확인
grep -n "swap\|swp" ~/Chamber_linux/linux-7.0-source/mm/internal.h
# compaction 관련 구조체 확인
grep -n "compact_control\|capture_control" ~/Chamber_linux/linux-7.0-source/mm/internal.h
핵심 자료구조
1. pagetable_move_control (파일:라인 46-56)
페이지 테이블 이동 연산의 상태를 관리하는 구조체입니다.
/* 페이지 테이블 이동 제어 구조체 */
struct pagetable_move_control {
struct vm_area_struct *old; /* 소스 VMA */
struct vm_area_struct *new; /* 대상 VMA */
unsigned long old_addr; /* 이동 시작 주소 */
unsigned long old_end; /* 이동 범위 끝 주소 (배타적) */
unsigned long new_addr; /* 이동 대상 주소 */
unsigned long len_in; /* 사용자가 지정한 바이트 수 */
bool need_rmap_locks; /* rmap 잠금 필요 여부 */
bool for_stack; /* 스택 이동 여부 */
};
2. GFP 플래그 마스크 (파일:라인 74-87)
할당 제약 조건을 정의하는 마스크들입니다.
/* 워터마크 체크 및 회수 동작에만 영향을 주는 플래그 */
#define GFP_RECLAIM_MASK (__GFP_RECLAIM|__GFP_HIGH|__GFP_IO|__GFP_FS|\
__GFP_NOWARN|__GFP_RETRY_MAYFAIL|__GFP_NOFAIL|\
__GFP_NORETRY|__GFP_MEMALLOC|__GFP_NOMEMALLOC|\
__GFP_NOLOCKDEP)
/* 부팅 초기에 허용되는 GFP 플래그 */
#define GFP_BOOT_MASK (__GFP_BITS_MASK & ~(__GFP_RECLAIM|__GFP_IO|__GFP_FS))
/* cpuset 및 노드 배치 제약 조건 제어 */
#define GFP_CONSTRAINT_MASK (__GFP_HARDWALL|__GFP_THISNODE)
/* slab 할당자에서 사용 불가 플래그 */
#define GFP_SLAB_BUG_MASK (__GFP_DMA32|__GFP_HIGHMEM|~__GFP_BITS_MASK)
3. WARN_ON_ONCE_GFP (파일:라인 92-101)
__GFP_NOWARN 지정 시 경고를 억제하는 매크로입니다.
/* __GFP_NOWARN 지정 시 경고 미발생 */
#define WARN_ON_ONCE_GFP(cond, gfp) ({ \
static bool __section(".data..once") __warned; \
int __ret_warn_once = !!(cond); \
if (unlikely(!(gfp & __GFP_NOWARN) && __ret_warn_once && !__warned)) { \
__warned = true; \
WARN_ON(1); \
} \
unlikely(__ret_warn_once); \
})
4. alloc_context (파일:라인 657-675)
페이지 할당 매개변수를 보관하는 구조체입니다.
/* 할당 컨텍스트 - 할당 함수 간 전달되는 매개변수 */
struct alloc_context {
struct zonelist *zonelist; /* 존 리스트 */
nodemask_t *nodemask; /* 노드 마스크 */
struct zoneref *preferred_zoneref; /* 선호 존 참조 */
int migratetype; /* 마이그레이션 유형 */
/*
* highest_zoneidx는 이 할당 요청에서 사용 가능한 최고 존 인덱스
* lower 존은 lowmem_reserve[highest_zoneidx]에 의해 보호됨
*/
enum zone_type highest_zoneidx;
bool spread_dirty_pages; /* 더티 페이지 확산 여부 */
};
5. compact_control (파일:라인 950-988)
메모리 컴팩션 연산을 추적하는 구조체입니다.
/* 컴팩션 컨트롤 - 마이그레이션 중인 페이지 관리 */
struct compact_control {
struct list_head freepages[NR_PAGE_ORDERS]; /* 마이그레이션 대상 빈 페이지 목록 */
struct list_head migratepages; /* 마이그레이션 중인 페이지 목록 */
unsigned int nr_freepages; /* 격리된 빈 페이지 수 */
unsigned int nr_migratepages; /* 마이그레이션할 페이지 수 */
unsigned long free_pfn; /* 격리 검색 기준PFN */
unsigned long migrate_pfn; /* 마이그레이션 검색 기준PFN */
unsigned long fast_start_pfn; /* 선형 스캔 시작PFN */
struct zone *zone; /* 대상 존 */
/* 통계 */
unsigned long total_migrate_scanned;
unsigned long total_free_scanned;
unsigned short fast_search_fail;
short search_order;
/* 할당 관련 */
const gfp_t gfp_mask;
int order;
int migratetype;
const unsigned int alloc_flags;
const int highest_zoneidx;
enum migrate_mode mode;
/* 제어 플래그 */
bool ignore_skip_hint;
bool no_set_skip_hint;
bool ignore_block_suitable;
bool direct_compaction;
bool proactive_compaction;
bool whole_zone;
bool contended;
bool finish_pageblock;
bool alloc_contig;
};
핵심 함수
1. folio_pte_batch_flags (파일:라인 337-390)
대형 folio에 대한 PTE 배치를 감지합니다.
static inline unsigned int folio_pte_batch_flags(struct folio *folio,
struct vm_area_struct *vma, pte_t *ptep, pte_t *ptentp,
unsigned int max_nr, fpb_t flags)
{
bool any_writable = false, any_young = false, any_dirty = false;
pte_t expected_pte, pte = *ptentp;
unsigned int nr, cur_nr;
/* 유효성 검사 */
VM_WARN_ON_FOLIO(!pte_present(pte), folio);
VM_WARN_ON_FOLIO(!folio_test_large(folio) || max_nr < 1, folio);
VM_WARN_ON_FOLIO(page_folio(pfn_to_page(pte_pfn(pte))) != folio, folio);
/* 실제 배치 크기 제한 */
max_nr = min_t(unsigned long, max_nr,
folio_pfn(folio) + folio_nr_pages(folio) - pte_pfn(pte));
/* 배치 감지 시작 */
nr = pte_batch_hint(ptep, pte);
expected_pte = __pte_batch_clear_ignored(pte_advance_pfn(pte, nr), flags);
ptep = ptep + nr;
/* 연속된 PTE 스캔 */
while (nr < max_nr) {
pte = ptep_get(ptep);
if (!pte_same(__pte_batch_clear_ignored(pte, flags), expected_pte))
break;
/* 쓰기 가능/접근/더티 플래그 병합 */
if (flags & FPB_MERGE_WRITE)
any_writable |= pte_write(pte);
if (flags & FPB_MERGE_YOUNG_DIRTY) {
any_young |= pte_young(pte);
any_dirty |= pte_dirty(pte);
}
cur_nr = pte_batch_hint(ptep, pte);
expected_pte = pte_advance_pfn(expected_pte, cur_nr);
ptep += cur_nr;
nr += cur_nr;
}
/* 플래그 업데이트 */
if (any_writable)
*ptentp = pte_mkwrite(*ptentp, vma);
if (any_young)
*ptentp = pte_mkyoung(*ptentp);
if (any_dirty)
*ptentp = pte_mkdirty(*ptentp);
return min(nr, max_nr);
}
PTE 배치 플래그 (fpb_t):
FPB_RESPECT_DIRTY: 더티 비트 비교 유지FPB_RESPECT_SOFT_DIRTY: 소프트 더티 비트 비교 유지FPB_RESPECT_WRITE: 쓰기 가능 비트 비교 유지FPB_MERGE_WRITE: 배치 내 쓰기 가능 여부 병합FPB_MERGE_YOUNG_DIRTY: 배치 내 접근/더티 병합2. swap_pte_batch (파일:라인 449-476)
스왑 PTE 배치를 감지합니다.
static inline int swap_pte_batch(pte_t *start_ptep, int max_nr, pte_t pte)
{
pte_t expected_pte = pte_next_swp_offset(pte);
const pte_t *end_ptep = start_ptep + max_nr;
const softleaf_t entry = softleaf_from_pte(pte);
pte_t *ptep = start_ptep + 1;
unsigned short cgroup_id;
VM_WARN_ON(max_nr < 1);
VM_WARN_ON(!softleaf_is_swap(entry));
cgroup_id = lookup_swap_cgroup_id(entry);
while (ptep < end_ptep) {
softleaf_t entry;
pte = ptep_get(ptep);
if (!pte_same(pte, expected_pte))
break;
entry = softleaf_from_pte(pte);
if (lookup_swap_cgroup_id(entry) != cgroup_id)
break;
expected_pte = pte_next_swp_offset(expected_pte);
ptep++;
}
return ptep - start_ptep;
}
3. buddy 함수들 (파일:라인 685-788)
Buddy 할당자의 핵심 유틸리티 함수들입니다.
/* 버디 순서 반환 */
static inline unsigned int buddy_order(struct page *page)
{
/* PageBuddy()는 호출자가 검증해야 함 */
return page_private(page);
}
/* 영역 잠금 없이 버디 순서 확인 */
#define buddy_order_unsafe(page) READ_ONCE(page_private(page))
/* 페이지가 유효한 버디인지 확인 */
static inline bool page_is_buddy(struct page *page, struct page *buddy,
unsigned int order)
{
if (!page_is_guard(buddy) && !PageBuddy(buddy))
return false;
if (buddy_order(buddy) != order)
return false;
/* 존 검사는 마지막에 수행 */
if (page_zone_id(page) != page_zone_id(buddy))
return false;
VM_BUG_ON_PAGE(page_count(buddy) != 0, buddy);
return true;
}
/* 버디 PFN 계산 (XOR 연산) */
static inline unsigned long
__find_buddy_pfn(unsigned long page_pfn, unsigned int order)
{
return page_pfn ^ (1 << order);
}
4. mlock/munlock 함수들 (파일:라인 1110-1146)
VMA 잠금 처리 함수들입니다.
void mlock_folio(struct folio *folio);
static inline void mlock_vma_folio(struct folio *folio,
struct vm_area_struct *vma)
{
/*
* VM_SPECIAL 체크 두 가지 목적:
* 1) VM_IO가 mlock 중 마이그레이션 이중 계산 방지
* 2) mmap_region()과 mlock_fixup()이 VM_LOCKED를
* VM_SPECIAL VMA에 남기지 않도록 보장
*/
if (unlikely((vma->vm_flags & (VM_LOCKED|VM_SPECIAL)) == VM_LOCKED))
mlock_folio(folio);
}
void munlock_folio(struct folio *folio);
static inline void munlock_vma_folio(struct folio *folio,
struct vm_area_struct *vma)
{
if (unlikely(vma->vm_flags & VM_LOCKED))
munlock_folio(folio);
}
5. vma_address 함수 (파일:라인 1159-1177)
페이지 범위의 가상 주소를 찾습니다.
static inline unsigned long vma_address(const struct vm_area_struct *vma,
pgoff_t pgoff, unsigned long nr_pages)
{
unsigned long address;
if (pgoff >= vma->vm_pgoff) {
address = vma->vm_start +
((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
/* VMA 범위 초과 또는 0 래핑 체크 */
if (address < vma->vm_start || address >= vma->vm_end)
address = -EFAULT;
} else if (pgoff + nr_pages - 1 >= vma->vm_pgoff) {
/* 32비트에서 0 래핑 가능성 회피 */
address = vma->vm_start;
} else {
address = -EFAULT;
}
return address;
}
호출 흐름
┌─────────────────────────────────────────────────────────────┐
│ MM 서브시스템 전체 │
├─────────────────────────────────────────────────────────────┤
│ page_alloc.c │ vmscan.c │ compaction.c │ rmap.c │
│ filemap.c │ mremap.c │ memory.c │ swap.c │
└────────┬───────────┴───────────────┴───────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ mm/internal.h │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 내부 구조체 (alloc_context, compact_control 등) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 유틸리티 함수 (buddy 함수, folio 함수 등) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 플래그 마스크 (GFP_* 마스크, ALLOC_* 상수) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 외부 공개 API │
│ mm.h, page_alloc.h, vmscan.h, compaction.h, rmap.h │
└─────────────────────────────────────────────────────────────┘
조건별 비교
internal.h 구성 요소 분류
| 분류 | 구조체/함수/매크로 | 용도 |
|---|---|---|
| 페이지 테이블 이동 | pagetable_move_control, move_page_tables | VMA 간 페이지 테이블 이동 |
| GFP 플래그 | GFP_RECLAIM_MASK, GFP_BOOT_MASK | 할당 제약 조건 정의 |
| 할당 컨텍스트 | alloc_context | 할당 함수 간 매개변수 전달 |
| 컴팩션 | compact_control, capture_control | 메모리 컴팩션 추적 |
| Buddy 시스템 | buddy_order, find_buddy_pfn, page_is_buddy | 버디 페이지 검색/검증 |
| PTE 배치 | folio_pte_batch_flags, swap_pte_batch | 대형 folio PTE 처리 최적화 |
| VMA 처리 | vma_address, mlock_vma_folio | 가상 메모리 영역 관리 |
| 할당 플래그 | ALLOC_WMARK_*, ALLOC_OOM | 존 워터마크 기반 할당 제어 |
GFP 플래그별 허용 범위
| 플래그 | 의미 | 사용 시점 |
|---|---|---|
| GFP_RECLAIM_MASK | 회수 관련 플래그 모음 | 직접 회수 시 |
| GFP_BOOT_MASK | 부팅 시 허용 플래그 | 조기 부팅 할당 |
| GFP_CONSTRAINT_MASK | 배치 제약 조건 | NUMA/cpuset 할당 |
| GFP_SLAB_BUG_MASK | slab에서 사용 불가 | slab 할당 시 |
ALLOC 플래그 동작
| 플래그 | 동작 | 조건 |
|---|---|---|
| ALLOC_WMARK_MIN | 최소 워터마크에서 할당 | 기본 할당 경로 |
| ALLOC_WMARK_LOW | 저 워터마크에서 할당 | kswapd 간접 회수 후 |
| ALLOC_WMARK_HIGH | 높은 워터마크에서 할당 | 정상 동작 |
| ALLOC_NO_WATERMARKS | 워터마크 무시 | 특수 할당 |
| ALLOC_OOM | OOM 상태 할당 | 메모리 부족 시 |
| ALLOC_NON_BLOCK | 차단 없이 할당 | 논블로킹 할당 |
| ALLOC_CMA | CMA 영역에서 할당 | 연속 메모리 할당 |