리눅스 Transparent Huge Pages 설정 읽는 법: enabled, 크기별 설정, madvise, khugepaged
THP 설정은 최상위 enabled와 크기별 hugepages-<size>kB/enabled로 나뉩니다. inherit, MADV_HUGEPAGE와 MADV_NOHUGEPAGE, khugepaged의 범위, /proc과 sysfs에서 실제 사용을 확인하는 방법을 커널 문서 기준으로 정리했습니다.
Transparent Huge Pages(THP)는 리눅스 커널이 가상 메모리를 기본 페이지보다 큰 페이지로 자동으로 채우는 기능입니다. 지금 커널 문서에는 /sys/kernel/mm/transparent_hugepage/enabled 말고도 크기마다 따로 정하는 설정이 있습니다. 그래서 최상위 파일만 읽고 “THP가 꺼져 있다” 또는 “켜져 있다”고 판단하면 틀릴 수 있습니다.
이 글은 2026년 10월 13일에 확인한 docs.kernel.org의 문서(페이지에 표시된 빌드 표시는 7.3.0-rc7)와 man-pages 6.19의 madvise(2)를 기준으로 합니다. 사용하는 커널이 다르면 파일이나 값이 없을 수 있으니, 그 커널 버전의 문서와 함께 읽어야 합니다. 아래 명령은 모두 읽기 전용입니다.
THP가 하는 일
커널 문서는 기본 페이지를 4KB, huge page를 2MB로 가정하고 설명합니다. 실제 크기는 CPU 아키텍처에 따라 다릅니다. 문서가 드는 이점은 두 가지입니다.
- 프로그램이 처음 건드리는 2MB 영역마다 페이지 폴트가 한 번만 일어나 커널 진입 횟수가 512분의 1로 줍니다. 이 효과는 매핑을 처음 접근할 때만 있고, 폴트마다 지우거나 복사할 페이지가 커지는 단점도 있습니다.
- 더 중요한 것은 이후의 모든 접근입니다. TLB 항목 하나가 훨씬 넓은 가상 메모리를 덮어 TLB 미스가 줄고, 미스가 나도 더 빨리 처리됩니다.
비용도 있습니다. 큰 영역을 mmap한 뒤 1바이트만 건드려도 4KB 대신 2MB가 할당될 수 있습니다. 시스템 전체가 아니라 MADV_HUGEPAGE로 표시한 영역에서만 THP를 쓰는 모드가 있는 이유가 이것입니다.
커널 문서 첫머리는 THP가 현재 익명 메모리와 tmpfs/shmem에서만 동작한다고 씁니다. 하지만 같은 문서의 khugepaged 설명과 madvise(2)는 조건을 갖춘 일반 파일 매핑을 PMD 크기로 합치는 경우도 다룹니다. 이 글은 주로 익명 메모리를 다룹니다.
최근 커널에는 multi-size THP(mTHP)도 있습니다. 기본 페이지보다 크고 PMD 크기(위 예에서 2MB)보다 작은 16K, 32K, 64K 같은 크기로 익명 메모리를 할당합니다. mTHP는 계속 PTE로 매핑되지만 페이지 폴트가 줄고, 페이지 하나가 작아서 폴트 때의 지연 급증은 PMD 크기보다 훨씬 덜하다고 문서는 설명합니다.
설정은 두 층으로 되어 있다
익명 메모리용 THP 설정은 두 곳에 있습니다.
| 파일 | 받는 값 | 기본값(문서 기준) |
|---|---|---|
/sys/kernel/mm/transparent_hugepage/enabled |
always, madvise, never |
이 문서에는 적혀 있지 않음 |
/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled |
always, madvise, never, inherit |
PMD 크기는 inherit, 나머지 크기는 never |
크기별 파일이 inherit이면 그 크기는 최상위 enabled 값을 따릅니다. inherit이 아니면 크기별 값이 그대로 적용됩니다. 어떤 크기의 디렉터리가 있는지는 시스템마다 다릅니다. 여러 크기가 켜져 있으면 커널이 할당마다 가장 알맞은 크기를 고릅니다.
각 값의 뜻은 다음과 같습니다.
always: 시스템 전체에서 THP를 씁니다.madvise:madvise(MADV_HUGEPAGE)로 표시한 영역에서만 씁니다.never: 이 설정으로는 THP를 쓰지 않습니다. 다만 아래에서 보듯 예외가 있습니다.
최상위 기본값은 이 문서에 나오지 않습니다. 부팅 매개변수 transparent_hugepage=always|madvise|never로 부팅 시 기본값을 바꿀 수 있다는 것만 적혀 있습니다. 크기별로는 thp_anon= 매개변수를 쓰는데, 이것을 한 번이라도 지정하면 명시하지 않은 크기는 PMD 크기까지 포함해 모두 never가 됩니다. 커널 명령줄에 thp_anon=이 있는지도 함께 봐야 하는 이유입니다.
현재 상태는 다음처럼 읽을 수 있습니다.
cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/hpage_pmd_size
grep -H . /sys/kernel/mm/transparent_hugepage/hugepages-*kB/enabled
cat /proc/cmdline
hpage_pmd_size는 PMD 크기 THP의 크기를 바이트 단위로 보여 줍니다. 커널 문서는 이 파일들에 값을 쓰는 예만 보여 주고, 읽었을 때 출력 형식은 설명하지 않습니다. hugepages-*kB 디렉터리가 하나도 없다면 이 글이 기준으로 삼은 문서와 다른 커널일 가능성이 크니, 그 버전의 문서를 확인하세요.
tmpfs와 shmem은 따로 설정합니다. shmem_enabled와 크기별 hugepages-<size>kB/shmem_enabled, tmpfs 마운트 옵션 huge=가 그것입니다. 이 글에서는 익명 메모리 쪽만 다룹니다.
모두 never여도 THP가 생길 수 있다
커널 문서는 sysfs의 THP 설정을 모두 never로 해도 THP가 완전히 꺼지지 않는다고 적고 있습니다. madvise(..., MADV_COLLAPSE)가 이 설정을 무시하고 해당 범위를 PMD 크기 huge page로 합치기 때문입니다. madvise(2) 매뉴얼도 MADV_COLLAPSE(Linux 6.1부터)는 /sys/kernel/mm/transparent_hugepage 아래의 어떤 설정과도 무관하게 동작한다고 설명합니다. 다만 MADV_NOHUGEPAGE가 걸린 영역은 존중합니다.
한 프로세스에서 THP를 완전히 끄는 방법으로 문서가 드는 것은 prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0)입니다. 전역 설정이나 MADV_COLLAPSE와 상관없이 그 프로세스의 THP를 끄고, 이 설정은 fork(2)와 execve(2)를 거쳐 상속됩니다. prctl(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, 0, 0)처럼 1 다음의 세 번째 인자로 PR_THP_DISABLE_EXCEPT_ADVISED를 주면 MADV_HUGEPAGE나 MADV_COLLAPSE로 요청한 영역에서만 THP를 허용합니다.
프로세스가 완전히 꺼진 상태인지는 /proc/<PID>/status의 THP_enabled로 봅니다. PR_SET_THP_DISABLE로 THP를 부분이 아니라 완전히 껐을 때 0이 됩니다.
grep THP_enabled /proc/<PID>/status
MADV_HUGEPAGE와 MADV_NOHUGEPAGE
madvise 모드에서 THP를 받으려면 애플리케이션이 영역을 직접 표시해야 합니다. 두 플래그 모두 Linux 2.6.38부터 있습니다.
MADV_HUGEPAGE는 지정한 범위에서 THP를 켭니다. 커널은 이렇게 표시된 영역을 주기적으로 훑어 huge page로 바꾸고, 영역이 huge page 크기에 맞게 정렬돼 있으면 처음부터 huge page를 할당하기도 합니다.MADV_NOHUGEPAGE는 지정한 범위가 THP로 채워지지 않게 합니다.
매뉴얼에 적힌 조건은 이렇습니다.
- 이 플래그들은 커널이
CONFIG_TRANSPARENT_HUGEPAGE로 빌드됐을 때만 쓸 수 있습니다. 파일과 shmem 메모리는CONFIG_READ_ONLY_THP_FOR_FS도 있어야 합니다. - 어떤 메모리든 huge page 경계에 맞는 부분만 교체됩니다. tmpfs를 포함한 파일 매핑은 파일 안에서도 정렬돼 있어야 하고, tmpfs가 아닌 파일은 쓰기용으로 열려 있지 않고 매핑이 실행 가능해야 합니다.
- 스택이나
VM_NOHUGEPAGE,VM_HUGETLB,VM_IO같은 플래그가 붙은 영역은 대상이 아닙니다.
어느 영역에 플래그가 걸렸는지는 /proc/<PID>/smaps의 VmFlags 줄에서 볼 수 있습니다. hg는 huge page advise 플래그, nh는 no huge page advise 플래그입니다.
매뉴얼은 MADV_HUGEPAGE가 메모리를 쉽게 낭비할 수 있다고 경고합니다. 접근 패턴을 미리 알고 메모리 사용량이 늘지 않는다고 판단한 영역에만 쓰라는 것입니다. 또 “흔한 커널 구성은 기본으로 MADV_HUGEPAGE 같은 동작을 제공한다”고 쓰는데, 실제 기본값은 배포판과 커널 빌드 설정에 달려 있으니 직접 확인해야 합니다. 이 매뉴얼은 크기별 mTHP 설정을 언급하지 않습니다.
defrag: 할당을 위해 얼마나 기다릴지
THP를 쓰기로 했어도 당장 비어 있는 큰 연속 메모리가 없을 수 있습니다. 이때의 동작은 /sys/kernel/mm/transparent_hugepage/defrag가 정합니다.
| 값 | 동작 |
|---|---|
always |
할당에 실패하면 그 자리에서 직접 회수와 compaction을 하며 기다립니다 |
defer |
kswapd와 kcompactd를 깨워 백그라운드에서 준비하고, 나중에 khugepaged가 THP를 채웁니다 |
defer+madvise |
MADV_HUGEPAGE 영역만 always처럼, 나머지는 defer처럼 동작합니다 |
madvise |
MADV_HUGEPAGE 영역만 직접 회수를 합니다. 문서상 기본값입니다 |
never |
정리를 시도하지 않고 바로 작은 페이지로 돌아갑니다 |
never로 해도 MADV_COLLAPSE는 THP를 만들 수 있습니다. 문서는 defrag에 CPU를 쓴 만큼 THP로 이득을 볼 것이라는 기대가 항상 맞지는 않는다고도 적고 있습니다.
khugepaged가 하는 일과 하지 않는 일
khugepaged는 메모리를 훑어 작은 페이지들을 huge page로 합치는 데몬입니다. 어느 크기든 THP가 켜지면(크기별 설정이나 최상위 설정이 always 또는 madvise) 자동으로 시작되고, 모든 크기가 꺼지면(크기별 설정과 최상위 설정이 모두 never) 멈춥니다.
범위에는 제한이 있습니다.
- 파일과 shmem은 PMD 크기로만 합칩니다. 다른 크기로 합치는 것은 익명 메모리뿐입니다.
- mTHP로 합칠 때는 공유되거나 스와프된 페이지가 하나라도 있으면 실패합니다.
MADV_COLLAPSE는 PMD 크기로만 합치고 mTHP는 시도하지 않습니다.
설정은 /sys/kernel/mm/transparent_hugepage/khugepaged/ 아래에 있습니다. pages_to_scan은 한 번에 훑는 페이지 수, scan_sleep_millisecs는 훑기 사이의 대기 시간, max_ptes_none은 합칠 때 허용하는 빈 페이지 수입니다. max_ptes_none이 높으면 메모리를 더 쓸 수 있고, 낮으면 합쳐지는 경우가 줄어듭니다. mTHP 합치기에서는 0과 HPAGE_PMD_NR - 1만 지원하며, 중간 값을 넣으면 경고를 내고 0으로 동작합니다.
khugepaged/pages_collapsed는 진행 정도를 대략 보여 주는 값일 뿐입니다. “합쳤다”는 말이 PTE 매핑을 PMD 매핑으로 바꾼 것일 수도, 물리 페이지를 huge page 하나로 바꾼 것일 수도 있기 때문입니다. 정확한 집계는 /proc/vmstat과 크기별 stats 디렉터리를 보라고 문서는 안내합니다.
최상위 enabled, 크기별 hugepages-<size>kB/enabled, tmpfs 마운트 옵션은 바꿔도 앞으로의 동작에만 영향을 줍니다. 이미 실행 중인 애플리케이션과 khugepaged에 등록된 영역에 반영하려면 THP를 쓰고 있었을 수 있는 애플리케이션을 다시 시작해야 합니다.
실제로 쓰이는지 확인하기
설정이 켜져 있다고 THP가 할당됐다는 뜻은 아닙니다. 사용량은 /proc에서 따로 봐야 합니다.
grep -E '^(AnonHugePages|ShmemHugePages|ShmemPmdMapped|FileHugePages|FilePmdMapped):' /proc/meminfo
grep -E '^(AnonHugePages|ShmemPmdMapped|FilePmdMapped):' /proc/<PID>/smaps_rollup
여기서 주의할 점이 있습니다. AnonHugePages는 역사적인 이유로 이런 이름이 붙었지만 PMD 크기로 매핑된 익명 THP만 셉니다. 문서는 원래 AnonHugePmdMapped라고 불렸어야 했다고 적습니다. smaps에는 PMD 수준에서 매핑되지 않은 THP를 위한 항목이 따로 없어서, 64K 같은 mTHP는 이 값에 나타나지 않습니다.
| 항목 | 위치 | 뜻 |
|---|---|---|
AnonHugePages |
meminfo, smaps | PMD 수준으로 매핑된 익명 THP |
ShmemHugePages |
meminfo | huge page로 할당된 shmem과 tmpfs 메모리 |
ShmemPmdMapped |
meminfo, smaps | huge page로 사용자 공간에 매핑된 shmem |
FileHugePages |
meminfo | huge page로 할당된 파일 데이터(페이지 캐시) |
FilePmdMapped |
meminfo, smaps | huge page로 매핑된 페이지 캐시(smaps에서는 shmem/tmpfs 제외) |
프로세스별로 볼 때는 smaps_rollup이 편합니다. 모든 매핑의 값을 합친 파일이고, 같은 정보를 smaps에서 직접 더하는 것보다 비용이 훨씬 적다고 문서에 나옵니다. smaps를 자주 읽으면 부담이 생긴다는 경고도 있습니다. 어느 매핑에 THP가 있는지 알아야 할 때만 smaps를 보면 됩니다.
smaps의 KernelPageSize와 MMUPageSize는 THP를 쓰고 있어도 바뀌지 않으니 판단 근거로 쓰면 안 됩니다. THPeligible이 1이면 그 매핑이 현재 켜진 크기 중 하나로 THP를 할당받을 자격이 있다는 뜻일 뿐, 실제로 할당됐다는 뜻은 아닙니다.
mTHP를 포함해 크기별로 보려면 hugepages-<size>kB/stats/ 아래 파일을 읽습니다.
grep -H . /sys/kernel/mm/transparent_hugepage/hugepages-*kB/stats/nr_anon
grep -H . /sys/kernel/mm/transparent_hugepage/hugepages-*kB/stats/anon_fault_alloc
grep -H . /sys/kernel/mm/transparent_hugepage/hugepages-*kB/stats/anon_fault_fallback
크기별 통계를 제공하지 않는 커널에서는 이 경로나 파일이 없을 수 있습니다. 그때는 위 명령이 일치하는 파일이 없다는 오류만 내고 값을 보여 주지 않습니다.
nr_anon은 그 크기의 익명 THP가 시스템 전체에 몇 개 있는지 보여 줍니다. 전부 매핑된 것도 있고 일부만 매핑되거나 쓰이지 않는 것도 있습니다. anon_fault_fallback은 그 크기의 할당이나 과금(charge)에 실패해 더 작은 크기나 기본 페이지로 물러난 횟수입니다.
/proc/vmstat 카운터가 말해 주는 것
grep -E '^(thp_|compact_(stall|success|fail) )' /proc/vmstat
| 카운터 | 늘어나는 때 |
|---|---|
thp_fault_alloc |
페이지 폴트에서 huge page 할당과 과금에 성공할 때 |
thp_fault_fallback |
페이지 폴트에서 huge page 할당이나 과금에 실패해 작은 페이지로 물러날 때 |
thp_collapse_alloc |
khugepaged가 합칠 범위를 찾고 새 huge page 할당에 성공할 때 |
thp_collapse_alloc_failed |
khugepaged가 합칠 범위를 찾았지만 할당에 실패할 때 |
thp_split_page |
huge page가 기본 페이지로 쪼개질 때. 오래된 huge page의 회수가 흔한 이유입니다 |
thp_file_alloc |
shmem huge page 할당에 성공할 때. 이름과 달리 shmem만 셉니다 |
compact_stall |
프로세스가 huge page를 얻으려고 compaction을 하며 멈출 때 |
이 카운터들은 사건이 일어날 때마다 늘어나는 값입니다. 한 번 읽은 값보다는 일정 간격으로 두 번 읽어 차이를 보는 편이 의미가 있습니다. thp_fault_fallback이 늘었다면 huge page를 원했지만 못 받은 폴트가 있었다는 뜻이고, compact_stall이 늘었다면 할당을 위해 멈춘 일이 있었다는 뜻입니다. 멈춘 시간은 이 카운터로 알 수 없습니다. 문서는 function tracer와 mm_page_alloc tracepoint로 측정하라고 안내합니다.
할당이 잘 된다는 사실이 애플리케이션이 빨라졌다는 증거가 되지는 않습니다. 성능 차이는 워크로드에서 따로 측정해야 하고, 이 카운터는 그 원인을 좁힐 때 쓰는 자료입니다.
확인 순서
enabled와hugepages-*kB/enabled를 함께 읽고,inherit인 크기가 최상위 값을 따른다는 점을 반영해 크기별 실제 정책을 정리합니다./proc/cmdline에서transparent_hugepage=와thp_anon=을 확인합니다.- 대상 프로세스의
THP_enabled와VmFlags의hg,nh를 봅니다. - meminfo와
smaps_rollup으로 PMD 크기 사용량을, 크기별stats로 mTHP를 확인합니다. /proc/vmstat의 fallback과 compaction 카운터를 시간 간격을 두고 비교합니다.
커널 문서에는 echo로 설정을 바꾸는 예가 많지만, 최상위와 크기별 enabled는 바꿔도 앞으로의 동작에만 적용되고 애플리케이션 재시작이 필요합니다. 운영 중인 시스템에서 바꾸기 전에 위 순서로 현재 상태부터 기록해 두세요.
/proc/meminfo의 다른 항목을 읽는 방법은 리눅스 free 명령에서 free가 거의 0일 때에 정리해 두었습니다.