DAMON (Data Access MONitor)
리눅스 커널의 DAMON(Data Access MONitor) 서브시스템을 심층 분석합니다. 리전(region) 기반 적응적 샘플링으로 오버헤드(Overhead)를 최소화하면서 메모리 접근 패턴을 모니터링하고, DAMOS(Data Access-aware Memory Operation Schemes)를 통해 콜드 페이지(Page) 회수, THP 프로모션, LRU 정렬 등을 자동으로 수행하는 구조를 커널 소스(mm/damon/) 기반으로 분석합니다. kdamond 커널 스레드(Kernel Thread), sysfs/debugfs 인터페이스, damo 유저스페이스 도구, NUMA 티어링 연동, MGLRU 비교, 파라미터 튜닝까지 포괄합니다.
핵심 요약
- 저오버헤드 접근 모니터링 -- PTE 접근 비트 샘플링으로 전체 페이지 스캔 없이 메모리 접근 패턴을 추적합니다. 일반적으로 CPU 오버헤드 1% 미만.
- 적응적 리전 -- 접근 빈도가 비슷한 연속 페이지를 하나의 리전으로 병합하고, 패턴이 달라지면 자동으로 분할합니다. min_nr_regions~max_nr_regions 범위 내에서 동적 조절.
- DAMOS 자동 정책 -- 모니터링 결과에 기반해 pageout, hugepage collapse, LRU 우선순위(Priority) 조정 등의 액션을 자동 수행합니다.
- 다중 Operations Set -- vaddr(가상 주소 공간(Address Space)), fvaddr(고정 가상 주소(Virtual Address)), paddr(물리 주소(Physical Address)) 세 가지 모니터링 대상을 지원합니다.
- sysfs 인터페이스 --
/sys/kernel/mm/damon/을 통해 유저스페이스에서 전체 DAMON 설정을 제어할 수 있습니다.
단계별 이해
- PTE 접근 비트 이해
CPU가 메모리 페이지에 접근하면 하드웨어가 자동으로 PTE의 Access Bit를 설정합니다. DAMON은 이 비트를 주기적으로 확인하고 리셋하여 접근 여부를 감지합니다. - 리전 모델 파악
연속된 주소 범위를 리전으로 묶고, 각 리전의 접근 횟수(nr_accesses)와 나이(age)를 추적하는 구조를 이해합니다. - 샘플링/집계 주기 구분
sampling_interval(샘플링 주기)과 aggr_interval(집계 주기)의 관계와 리전 분할/병합 타이밍을 파악합니다. - DAMOS 스킴 설정
접근 빈도와 나이 조건에 매칭되는 리전에 대해 어떤 액션을 수행할지 정의합니다. - 실전 적용과 모니터링
damo 도구로 워크로드를 프로파일링(Profiling)하고, sysfs로 DAMOS 스킴을 설정하여 메모리 효율을 최적화합니다.
mm/damon/core.c (핵심 모니터링 로직), mm/damon/ops-common.c (공통 연산), mm/damon/vaddr.c (가상 주소 모니터링), mm/damon/paddr.c (물리 주소 모니터링), mm/damon/sysfs.c (sysfs 인터페이스), mm/damon/reclaim.c (프로액티브 회수), mm/damon/lru_sort.c (LRU 정렬).
SeongJae Park이 DAMON의 주요 개발자이며, v5.15에서 커널에 최초 머지되었습니다.
DAMON 개요
DAMON(Data Access MONitor)은 리눅스 커널 v5.15에서 도입된 데이터 접근 모니터링 프레임워크입니다. 기존의 메모리 관리 메커니즘(LRU, kswapd)이 모든 페이지를 균일하게 처리하는 것과 달리, DAMON은 실제 접근 패턴을 정밀하게 추적하여 데이터 기반 메모리 최적화를 가능하게 합니다.
왜 DAMON이 필요한가
| 기존 메커니즘 | 한계 | DAMON의 해결 |
|---|---|---|
| LRU 리스트 | 접근 빈도 구분 불가 (active/inactive 이진 분류) | nr_accesses로 접근 빈도 정량화 |
| kswapd/직접 회수(Direct Reclaim) | 메모리 압박 시에만 동작 (사후 대응) | DAMOS로 사전 예방적 회수 가능 |
| /proc/PID/pagemap | 전체 페이지 테이블 순회 필요 (고비용) | 리전 기반 샘플링으로 O(regions) 비용 |
| perf mem | 하드웨어 PMU 의존, 오프라인 분석 | 커널 내장, 실시간(Real-time) 자동 대응 |
| idle page tracking | 사용자 도구 폴링(Polling) 필요, DAMOS 부재 | 모니터링 + 자동 액션 통합 |
소스 트리 구조
mm/damon/
├── core.c # 핵심: kdamond, 리전 관리, 샘플링/집계 루프
├── ops-common.c # Operations Set 공통 헬퍼
├── vaddr.c # vaddr operations: PTE 접근 비트 기반
├── paddr.c # paddr operations: 물리 주소 기반
├── sysfs.c # /sys/kernel/mm/damon/ 인터페이스
├── sysfs-schemes.c # DAMOS sysfs 스킴 관리
├── dbgfs.c # debugfs 인터페이스 (레거시)
├── reclaim.c # DAMON_RECLAIM 모듈
└── lru_sort.c # DAMON_LRU_SORT 모듈
아키텍처
DAMON은 4계층 아키텍처로 구성됩니다: kdamond(커널 스레드) → context(모니터링 컨텍스트) → target(모니터링 대상) → region(주소 범위). 이 계층 구조가 DAMON의 유연성과 확장성을 제공합니다.
/* include/linux/damon.h - 핵심 자료 구조 관계 (단순화) */
struct damon_region {
unsigned long ar.start; /* 리전 시작 주소 */
unsigned long ar.end; /* 리전 끝 주소 */
unsigned int nr_accesses; /* 집계 주기 내 접근 횟수 */
unsigned int age; /* nr_accesses 불변 지속 집계 횟수 */
struct list_head list; /* target 내 리전 연결 리스트 */
};
struct damon_target {
struct pid *pid; /* vaddr: 모니터링 대상 PID */
unsigned int nr_regions; /* 현재 리전 수 */
struct list_head regions_list; /* damon_region 연결 리스트 */
struct list_head list; /* context 내 target 연결 리스트 */
};
struct damon_ctx {
struct damon_attrs attrs; /* 샘플링/집계/갱신 주기 */
struct damon_operations ops; /* operations set (vaddr/paddr) */
struct list_head adaptive_targets; /* damon_target 리스트 */
struct list_head schemes; /* DAMOS 스킴 리스트 */
struct damon_callback callback; /* 사용자 콜백 */
};
설명
damon_region은 연속된 주소 범위의 접근 패턴을 추적하는 기본 단위입니다.
nr_accesses는 하나의 집계 주기(aggr_interval) 동안 접근이 감지된 샘플링 횟수이며,
age는 nr_accesses 값이 변하지 않은 채 지속된 집계 주기 수입니다.
damon_target은 프로세스(vaddr) 또는 물리 주소 범위(paddr)를 나타내며, 내부에 리전 리스트를 유지합니다.
damon_ctx가 모니터링의 전체 설정(Operations Set, DAMOS 스킴, 콜백(Callback))을 보유합니다.
리전 기반 적응적 샘플링
DAMON의 핵심 혁신은 리전 기반 적응적 샘플링(Region-based Adaptive Sampling)입니다. 모든 페이지를 순회하는 대신, 각 리전에서 하나의 랜덤 페이지만 샘플링하여 접근 여부를 확인합니다.
샘플링 알고리즘
DAMON의 모니터링 루프는 세 가지 시간 단위로 동작합니다:
| 파라미터 | 기본값 | 역할 |
|---|---|---|
sample_interval | 5ms | 각 리전에서 랜덤 페이지 1개의 PTE 접근 비트 확인 주기 |
aggr_interval | 100ms | nr_accesses 집계 및 DAMOS 스킴 적용 주기 |
update_interval | 60s | 모니터링 대상 리전 초기화/갱신 주기 (VMA 변경 반영) |
리전 분할과 병합
DAMON은 각 aggr_interval 종료 시 리전을 동적으로 분할/병합하여 정밀도를 자동 조절합니다:
- 분할(Split) -- 리전 크기가 크고 내부 접근 패턴이 불균일하면 2~3개로 분할합니다. 새 리전의 nr_accesses는 부모의 값을 상속받습니다.
- 병합(Merge) -- 인접한 두 리전의 nr_accesses 차이가
max_nr_regions기반 임계값 이하이면 병합합니다.
/* mm/damon/core.c - damon_merge_regions_of() (단순화) */
static void damon_merge_regions_of(
struct damon_target *t,
unsigned int threshold, /* nr_accesses 차이 임계값 */
unsigned long sz_limit) /* 병합 후 최대 크기 */
{
struct damon_region *r, *prev = NULL;
damon_for_each_region(r, t) {
if (prev && prev->ar.end == r->ar.start &&
abs(prev->nr_accesses - r->nr_accesses) <= threshold &&
damon_sz_region(prev) + damon_sz_region(r) <= sz_limit) {
prev->ar.end = r->ar.end;
prev->nr_accesses =
(prev->nr_accesses + r->nr_accesses) / 2;
damon_destroy_region(r, t);
} else {
prev = r;
}
}
}
설명
인접한 두 리전의nr_accesses 차이가 임계값 이하이고, 병합 후 크기가 제한 이내이면 하나로 합칩니다.
이 과정으로 접근 패턴이 균일한 대규모 영역은 하나의 리전으로 합쳐져 오버헤드가 줄고,
패턴이 변하는 경계 지점은 분할되어 정밀도가 높아집니다.
샘플링 오버헤드 분석
| 파라미터 | 워크로드 300MB RSS | 일반 페이지 스캔 | DAMON 리전 샘플링 |
|---|---|---|---|
| 모니터링 대상 페이지 수 | 76,800 | 76,800 PTE 확인 | ~100 리전 × 1 PTE = ~100 |
| 샘플링 당 비용 | - | O(페이지 수) | O(리전 수) |
| CPU 오버헤드 | - | ~5-10% | <1% |
| 정밀도 | - | 정확 | 통계적 추정 (95%+) |
min_nr_regions=10, max_nr_regions=1000이 기본값입니다. 리전 수가 많을수록 정밀하지만 오버헤드가 증가합니다. 대부분의 워크로드에서 기본값이 적절하며, 매우 큰 워크로드(수십 GB RSS)에서는 max_nr_regions=2000까지 늘릴 수 있습니다.
Operations Set
DAMON은 Operations Set 추상화를 통해 다양한 주소 공간 타입을 지원합니다. 각 Operations Set은 리전 초기화, 접근 비트 확인, DAMOS 액션 실행 등의 콜백을 구현합니다.
| Operations Set | 대상 | 접근 감지 방식 | 용도 |
|---|---|---|---|
vaddr | 프로세스 가상 주소 공간 | PTE Access Bit | 특정 프로세스의 메모리 접근 패턴 분석 |
fvaddr | 고정 가상 주소 범위 | PTE Access Bit | 사용자 지정 주소 범위 모니터링 |
paddr | 물리 주소 공간 | rmap을 통한 PTE 확인 | 시스템 전체 메모리 패턴, DAMON_RECLAIM |
/* include/linux/damon.h - Operations Set 인터페이스 */
struct damon_operations {
enum damon_ops_id id;
/* 모니터링 대상 리전 초기화 */
void (*init)(struct damon_ctx *ctx);
/* 모니터링 대상 갱신 (VMA 변경 반영) */
void (*update)(struct damon_ctx *ctx);
/* 리전의 접근 준비 (Access Bit 클리어) */
void (*prepare_access_checks)(struct damon_ctx *ctx);
/* 리전의 접근 확인 (Access Bit 읽기) */
unsigned int (*check_accesses)(struct damon_ctx *ctx);
/* 리셋 (집계 완료 후) */
void (*reset_aggregated)(struct damon_ctx *ctx);
/* DAMOS 액션 적용 (pageout, hugepage 등) */
unsigned long (*apply_scheme)(struct damon_ctx *ctx,
struct damon_target *t,
struct damon_region *r,
struct damos *s);
/* 스킴 스코어 계산 (우선순위 결정) */
int (*get_scheme_score)(struct damos *s,
struct damon_region *r);
};
설명
damon_operations는 Strategy 패턴으로 구현되어, 동일한 모니터링 로직에 다른 주소 공간 구현을 끼워넣을 수 있습니다.
prepare_access_checks()에서 PTE 접근 비트를 클리어하고, 다음 check_accesses()에서 비트가 다시 설정되었는지 확인합니다.
vaddr은 mm_struct의 페이지 테이블을 직접 워크하고, paddr은 folio_get_anon_vma() + rmap_walk()로 물리 페이지의 매핑(Mapping)을 역추적(Backtrace)합니다.
vaddr 동작 상세
/* mm/damon/vaddr.c - PTE Access Bit 확인 (단순화) */
static void damon_va_check_access(
struct damon_ctx *ctx,
struct damon_target *t,
struct damon_region *r,
struct mm_struct *mm)
{
unsigned long addr;
pte_t *pte;
/* 리전 내 랜덤 주소 선택 */
addr = r->sampling_addr;
/* 페이지 테이블 워크로 PTE 획득 */
pte = damon_get_pte(mm, addr);
if (!pte)
return;
/* Access Bit 확인 */
if (pte_young(*pte)) {
r->nr_accesses++;
/* Access Bit 클리어 (다음 확인을 위해) */
pte_mkold(*pte);
}
}
paddr 동작 상세
paddr Operations Set은 물리 주소 공간을 직접 모니터링합니다. 물리 페이지에 대한 접근 여부를 확인하려면 rmap(reverse mapping)을 통해 해당 물리 페이지를 매핑하는 모든 PTE를 역추적해야 합니다.
/* mm/damon/paddr.c - 물리 주소 접근 확인 (단순화) */
static bool damon_pa_young(unsigned long paddr,
unsigned long *folio_sz)
{
struct folio *folio;
bool young = false;
folio = damon_get_folio(PHYS_PFN(paddr));
if (!folio)
return false;
*folio_sz = folio_size(folio);
/* 1. folio 자체의 reference bit 확인 */
if (folio_test_young(folio) ||
!folio_test_idle(folio)) {
young = true;
goto out;
}
/* 2. rmap으로 모든 매핑의 PTE 확인 */
if (folio_mapped(folio)) {
struct damon_young_walk_private pvt = {
.young = false,
.folio_sz = folio_sz,
};
rmap_walk(folio, &damon_young_rwc);
young = pvt.young;
}
out:
folio_put(folio);
return young;
}
설명
paddr ops에서 접근 확인은 vaddr보다 복잡합니다.
물리 페이지가 여러 프로세스에 공유 매핑되어 있을 수 있으므로, rmap_walk()으로 모든 역방향 매핑의 PTE를 검사해야 합니다.
하나라도 Access Bit가 설정되어 있으면 해당 물리 페이지는 "accessed"로 판정합니다.
이 과정이 비용이 더 들지만, 시스템 전체의 물리 메모리(Physical Memory) 접근 패턴을 파악할 수 있다는 장점이 있습니다.
DAMON_RECLAIM이 paddr ops를 사용하는 이유가 바로 이것입니다.
fvaddr Operations Set
fvaddr(fixed virtual address)는 vaddr의 변형으로, VMA 변경에 따른 리전 재초기화를 하지 않습니다. 사용자가 지정한 고정 주소 범위만 모니터링하므로, 특정 메모리 영역(예: 공유 메모리 세그먼트, mmap 파일)의 접근 패턴을 추적할 때 유용합니다.
# fvaddr 사용 예시: 특정 mmap 영역만 모니터링
echo fvaddr > .../contexts/0/operations
# 대상 프로세스 설정
echo 1 > .../contexts/0/targets/nr_targets
echo $(pidof my-app) > .../contexts/0/targets/0/pid_target
# 초기 리전 수동 설정 (mmap 영역 주소)
echo 1 > .../contexts/0/targets/0/regions/nr_regions
echo 0x7f0000000000 > .../contexts/0/targets/0/regions/0/start
echo 0x7f0100000000 > .../contexts/0/targets/0/regions/0/end
콜백 기반 모니터링
DAMON은 모니터링 루프의 주요 지점에서 콜백 함수를 호출할 수 있습니다. 이를 통해 커널 내부 모듈이나 BPF 프로그램이 DAMON의 모니터링 데이터를 활용할 수 있습니다.
/* include/linux/damon.h - 콜백 구조체 */
struct damon_callback {
void *private;
/* 각 샘플링 주기 후 호출 */
int (*after_wmarks_check)(struct damon_ctx *ctx);
/* 각 집계 주기 후 호출 */
int (*after_aggregation)(struct damon_ctx *ctx);
/* kdamond 종료 전 호출 */
void (*before_terminate)(struct damon_ctx *ctx);
};
설명
after_aggregation은 가장 자주 사용되는 콜백입니다. 이 시점에서 모든 리전의 nr_accesses가 확정되어 있으므로 접근 패턴 분석에 적합합니다.
콜백이 0이 아닌 값을 반환하면 kdamond가 모니터링을 중단합니다. 이를 통해 특정 조건(예: 충분한 데이터 수집)에서 자동 종료가 가능합니다.
DAMON_RECLAIM과 DAMON_LRU_SORT는 이 콜백 메커니즘을 사용하지 않고 DAMOS를 직접 활용합니다.
| 콜백 | 호출 시점 | 활용 예시 |
|---|---|---|
after_wmarks_check | 워터마크(Watermark) 확인 후 | 모니터링 시작/중지 제어 |
after_aggregation | 집계 완료 후 | 접근 패턴 기록, 히트맵 생성, BPF 프로그램 |
before_terminate | kdamond 종료 전 | 리소스 정리, 최종 통계 출력 |
DAMOS (Data Access-aware Memory Operation Schemes)
DAMOS는 DAMON의 모니터링 결과를 자동으로 메모리 관리 액션에 연결하는 프레임워크입니다. "접근 빈도가 N 이하이고 age가 M 이상인 리전에 대해 pageout을 수행하라"와 같은 선언적 정책을 정의할 수 있습니다.
/* include/linux/damon.h - DAMOS 스킴 구조체 (단순화) */
struct damos {
/* 패턴 매칭 조건 */
struct damos_access_pattern pattern; /* min/max nr_accesses, age, size */
/* 수행할 액션 */
enum damos_action action; /* DAMOS_PAGEOUT, DAMOS_HUGEPAGE 등 */
/* 적용 제한 */
struct damos_quota quota; /* time/size 쿼터 */
/* 활성화 조건 */
struct damos_watermarks wmarks; /* free memory 기반 활성화 */
/* 필터 리스트 */
struct list_head filters; /* damos_filter 연결 리스트 */
/* 통계 */
struct damos_stat stat; /* 적용 횟수/크기 */
};
DAMOS 액션
DAMOS가 지원하는 메모리 관리 액션은 다음과 같습니다:
| 액션 | 커널 버전 | 설명 | 주요 용도 |
|---|---|---|---|
DAMOS_PAGEOUT | v5.16+ | 매칭된 리전의 페이지를 스왑(Swap)/디스크로 방출 | 콜드 페이지 프로액티브 회수 |
DAMOS_HUGEPAGE | v5.16+ | 매칭된 리전을 THP로 프로모션 (khugepaged 힌트) | 핫 페이지 THP 통합 |
DAMOS_NOHUGEPAGE | v5.16+ | 매칭된 리전의 THP 프로모션 억제 | 콜드/랜덤 접근 영역 THP 방지 |
DAMOS_LRU_PRIO | v6.0+ | LRU 리스트에서 우선순위 상향 (활성화) | 핫 페이지 보호 |
DAMOS_LRU_DEPRIO | v6.0+ | LRU 리스트에서 우선순위 하향 (비활성화) | 콜드 페이지 빠른 회수 대상화 |
DAMOS_STAT | v5.16+ | 통계만 수집, 실제 액션 없음 | 모니터링 전용, 정책 시뮬레이션 |
DAMOS_MIGRATE_HOT | v6.9+ | 핫 페이지를 빠른 NUMA 노드로 이동 | NUMA 티어링 프로모션 |
DAMOS_MIGRATE_COLD | v6.9+ | 콜드 페이지를 느린 NUMA 노드로 이동 | NUMA 티어링 디모션 |
/* mm/damon/paddr.c - pageout 액션 구현 (단순화) */
static unsigned long damon_pa_pageout(
struct damon_region *r)
{
unsigned long addr, applied = 0;
for (addr = r->ar.start; addr < r->ar.end; addr += PAGE_SIZE) {
struct folio *folio = damon_get_folio(PHYS_PFN(addr));
if (!folio)
continue;
/* 이미 회수 불가능하면 스킵 */
if (folio_test_unevictable(folio))
goto next;
/* 회수 리스트에 추가 */
list_add(&folio->lru, &folio_list);
applied += folio_nr_pages(folio);
next:
folio_put(folio);
}
/* 일괄 회수 수행 */
reclaim_pages(&folio_list);
return applied * PAGE_SIZE;
}
설명
DAMOS_PAGEOUT은 매칭된 리전의 모든 페이지를 순회하며 reclaim_pages()를 호출합니다.
이때 folio_test_unevictable()로 mlock된 페이지를 건너뛰고, anonymous 페이지는 스왑으로, file-backed 페이지는 디스크로 방출합니다.
paddr Operations Set에서는 물리 주소를 직접 사용하므로 rmap 역추적이 필요하지만, vaddr에서는 VMA를 통해 직접 처리합니다.
DAMOS 필터
DAMOS 필터는 패턴 매칭을 통과한 리전 중에서 특정 조건의 페이지를 추가로 포함하거나 제외합니다. 커널 v6.3에서 도입되어 지속적으로 필터 타입이 추가되고 있습니다.
| 필터 타입 | 설명 | 예시 용도 |
|---|---|---|
anon | 익명 페이지(Anonymous Page)만 포함/제외 | 파일 캐시(Cache)는 건드리지 않고 anonymous만 회수 |
memcg | 특정 cgroup의 페이지만 포함/제외 | 특정 컨테이너(Container)의 메모리만 최적화 |
addr | 특정 주소 범위 포함/제외 | 힙 영역만 타겟팅 |
young | 최근 접근된 페이지 포함/제외 | 아직 따뜻한 페이지는 회수 제외 |
target | 특정 모니터링 대상만 포함/제외 | 다중 프로세스 중 하나만 적용 |
/* include/linux/damon.h - 필터 구조체 */
struct damos_filter {
enum damos_filter_type type;
bool matching; /* true: 매칭 시 포함, false: 매칭 시 제외 */
union {
unsigned short memcg_id;
struct damon_addr_range addr_range;
int target_idx;
};
struct list_head list;
};
설명
matching 필드가 true이면 필터 조건에 매칭되는 페이지만 액션 대상으로 포함하고, false이면 매칭되는 페이지를 제외합니다.
필터는 리스트로 연결되어 순서대로 평가되며, 첫 번째 매칭 필터의 결과가 적용됩니다.
예를 들어 {type=anon, matching=true} 필터를 설정하면 anonymous 페이지에만 액션이 적용됩니다.
DAMOS 쿼터와 우선순위
DAMOS 쿼터는 스킴이 한 번의 집계 주기에서 소비할 수 있는 시간과 크기를 제한합니다. 쿼터가 초과되면 나머지 리전은 다음 주기로 연기됩니다. 이때 우선순위(prioritization)가 어떤 리전을 먼저 처리할지 결정합니다.
/* include/linux/damon.h - 쿼터 구조체 */
struct damos_quota {
unsigned long ms; /* 시간 제한 (밀리초, 0=무제한) */
unsigned long sz; /* 크기 제한 (바이트, 0=무제한) */
unsigned long reset_interval_ms; /* 쿼터 리셋 주기 */
/* 우선순위 가중치 */
unsigned int weight_sz; /* 리전 크기 가중치 */
unsigned int weight_nr_accesses; /* 접근 횟수 가중치 */
unsigned int weight_age; /* 나이 가중치 */
/* 자동 튜닝 목표 */
struct damos_quota_goal *goals; /* v6.8+: 자동 쿼터 조절 */
/* 내부 상태 */
unsigned long charged_sz;
unsigned long charged_from;
unsigned long esz; /* 유효 쿼터 크기 */
};
damos_quota_goal을 설정하면 DAMON이 목표 메트릭(예: PSI some 값 5% 이하)을 달성하도록 쿼터를 자동으로 조절합니다. 메모리 압박이 심하면 쿼터를 늘리고, 안정화되면 줄이는 피드백 루프입니다.
sysfs 인터페이스
DAMON의 주요 인터페이스는 /sys/kernel/mm/damon/admin/ 디렉토리입니다. 커널 v5.18에서 도입되어 debugfs 인터페이스를 대체합니다.
/sys/kernel/mm/damon/admin/
├── kdamonds/
│ ├── nr_kdamonds # kdamond 스레드 수 설정
│ └── 0/
│ ├── state # on/off/commit/update_schemes_stats
│ ├── pid # kdamond 스레드 PID (읽기 전용)
│ └── contexts/
│ ├── nr_contexts
│ └── 0/
│ ├── operations # vaddr/fvaddr/paddr
│ ├── monitoring_attrs/
│ │ ├── intervals/
│ │ │ ├── sample_us # 샘플링 주기 (마이크로초)
│ │ │ ├── aggr_us # 집계 주기
│ │ │ └── update_us # 갱신 주기
│ │ └── nr_regions/
│ │ ├── min # 최소 리전 수
│ │ └── max # 최대 리전 수
│ ├── targets/
│ │ ├── nr_targets
│ │ └── 0/
│ │ ├── pid_target # 대상 PID (vaddr)
│ │ └── regions/ # 초기 리전 (선택)
│ └── schemes/
│ ├── nr_schemes
│ └── 0/
│ ├── action # pageout/hugepage/...
│ ├── access_pattern/
│ ├── quotas/
│ ├── watermarks/
│ ├── filters/
│ └── stats/ # 적용 통계
sysfs 설정 워크플로
# 1. kdamond 생성
echo 1 > /sys/kernel/mm/damon/admin/kdamonds/nr_kdamonds
# 2. context 생성 및 operations 설정
echo 1 > /sys/kernel/mm/damon/admin/kdamonds/0/contexts/nr_contexts
echo vaddr > /sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/operations
# 3. 모니터링 파라미터 설정
echo 5000 > .../contexts/0/monitoring_attrs/intervals/sample_us # 5ms
echo 100000 > .../contexts/0/monitoring_attrs/intervals/aggr_us # 100ms
echo 60000000 > .../contexts/0/monitoring_attrs/intervals/update_us # 60s
# 4. 모니터링 대상 설정
echo 1 > .../contexts/0/targets/nr_targets
echo $(pidof my-app) > .../contexts/0/targets/0/pid_target
# 5. DAMOS 스킴 설정 (콜드 페이지 회수)
echo 1 > .../contexts/0/schemes/nr_schemes
echo pageout > .../contexts/0/schemes/0/action
echo 0 > .../contexts/0/schemes/0/access_pattern/nr_accesses/min
echo 0 > .../contexts/0/schemes/0/access_pattern/nr_accesses/max
echo 5 > .../contexts/0/schemes/0/access_pattern/age/min
# 6. 커밋 및 시작
echo commit > /sys/kernel/mm/damon/admin/kdamonds/0/state
echo on > /sys/kernel/mm/damon/admin/kdamonds/0/state
# 7. 통계 확인
echo update_schemes_stats > /sys/kernel/mm/damon/admin/kdamonds/0/state
cat .../contexts/0/schemes/0/stats/nr_applied
cat .../contexts/0/schemes/0/stats/sz_applied
설명
sysfs 인터페이스는 DAMON의 전체 설정을 디렉토리 계층 구조로 노출합니다.state 파일에 commit을 쓰면 현재 sysfs 설정이 실제 DAMON 설정에 반영되고,
on을 쓰면 kdamond 스레드(Thread)가 시작됩니다.
update_schemes_stats를 쓰면 DAMOS 통계가 갱신되어 stats/ 디렉토리에서 읽을 수 있습니다.
debugfs 인터페이스 (레거시)
초기 DAMON은 /sys/kernel/debug/damon/ debugfs 인터페이스를 사용했습니다. v5.18에서 sysfs 인터페이스가 도입된 이후 debugfs는 레거시로 분류되어 향후 제거될 예정입니다.
# debugfs 인터페이스 (레거시 - 신규 사용 비권장)
/sys/kernel/debug/damon/
├── attrs # "sample_us aggr_us update_us min_nr max_nr" 형식
├── target_ids # 대상 PID 목록 (공백 구분)
├── schemes # DAMOS 스킴 (한 줄에 하나씩)
└── monitor_on # "on"/"off"
# 예시: debugfs로 모니터링 시작
echo "5000 100000 60000000 10 1000" > /sys/kernel/debug/damon/attrs
echo "$(pidof my-app)" > /sys/kernel/debug/damon/target_ids
echo "on" > /sys/kernel/debug/damon/monitor_on
/sys/kernel/mm/damon/admin/)를 사용하세요.
DAMON 기반 프로액티브 회수
DAMON_RECLAIM은 DAMON의 가장 대표적인 활용 사례로, 콜드 페이지를 메모리 압박 없이 사전에 회수합니다. 기존 kswapd가 워터마크 기반으로 사후 대응하는 것과 달리, DAMON_RECLAIM은 접근 패턴을 분석하여 실제로 사용되지 않는 페이지만 선별적으로 회수합니다.
# DAMON_RECLAIM 활성화
echo Y > /sys/module/damon_reclaim/parameters/enabled
# 기본 파라미터 확인
cat /sys/module/damon_reclaim/parameters/sample_interval # 5000 (us)
cat /sys/module/damon_reclaim/parameters/aggr_interval # 100000 (us)
cat /sys/module/damon_reclaim/parameters/min_age # 200
# 서버 환경: 콜드 임계값을 낮추고 쿼터를 넉넉하게
echo 100 > /sys/module/damon_reclaim/parameters/min_age # 10초 미접근 시 콜드
echo 20 > /sys/module/damon_reclaim/parameters/quota_ms # 20ms/주기 처리
echo 256000000 > /sys/module/damon_reclaim/parameters/quota_sz # 256MB/주기
# 모니터링 통계 확인
cat /sys/module/damon_reclaim/parameters/nr_reclaimed # 회수된 페이지 수
cat /sys/module/damon_reclaim/parameters/bytes_reclaimed # 회수된 바이트 수
DAMON LRU Sort
DAMON_LRU_SORT(v6.0+)는 LRU 리스트의 페이지 순서를 DAMON의 접근 패턴 데이터 기반으로 재정렬합니다. 기존 LRU는 페이지 폴트(Page Fault)와 참조 시점에만 순서가 갱신되지만, DAMON_LRU_SORT는 실제 접근 빈도를 반영하여 보다 정확한 LRU 순서를 유지합니다.
| 동작 | 조건 | LRU 효과 |
|---|---|---|
| 핫 페이지 프로모션 | nr_accesses > hot_threshold | LRU active 리스트 head로 이동 |
| 콜드 페이지 디모션 | nr_accesses < cold_threshold, age > min_age | LRU inactive 리스트 tail로 이동 |
# DAMON_LRU_SORT 활성화
echo Y > /sys/module/damon_lru_sort/parameters/enabled
# 핫/콜드 임계값 설정
echo 5 > /sys/module/damon_lru_sort/parameters/hot_thres_access_freq # 접근 빈도 5 이상이면 핫
echo 200 > /sys/module/damon_lru_sort/parameters/cold_min_age # 200 주기 이상 미접근이면 콜드
# 쿼터 설정 (LRU 재정렬 비용 제한)
echo 10 > /sys/module/damon_lru_sort/parameters/quota_ms
# 통계 확인
cat /sys/module/damon_lru_sort/parameters/nr_lru_sort_hot # 핫 프로모션 횟수
cat /sys/module/damon_lru_sort/parameters/nr_lru_sort_cold # 콜드 디모션 횟수
damo 유저스페이스 도구
damo는 DAMON의 공식 유저스페이스 CLI 도구입니다. Python으로 작성되어 있으며, 모니터링 설정, 데이터 수집, 시각화, DAMOS 테스트를 간편하게 수행할 수 있습니다.
# 설치
pip3 install damo
# 프로세스 모니터링 (vaddr)
sudo damo start $(pidof my-app)
# 시스템 전체 모니터링 (paddr)
sudo damo start --ops paddr
# 접근 패턴 기록 (10초)
sudo damo record $(pidof my-app) --duration 10s -o access.json
# 히트맵 시각화
sudo damo report heats --input access.json
# 워킹셋 크기 분석
sudo damo report wss --input access.json
# 출력 예시:
# percentile working_set_size
# 0 23.4MB
# 25 45.2MB
# 50 67.8MB
# 75 89.3MB
# 100 156.7MB
# 핫/콜드 분포 리포트
sudo damo report nr_regions --input access.json
# DAMOS 시뮬레이션 (실제 액션 없이 예상 결과만 확인)
sudo damo schemes --action stat \
--access_rate 0 0 \
--age 200 max \
$(pidof my-app)
# DAMOS 적용
sudo damo schemes --action pageout \
--access_rate 0 0 \
--age 200 max \
--quotas 10ms 128M \
$(pidof my-app)
설명
damo record는 DAMON 모니터링을 시작하고 접근 패턴 데이터를 JSON 파일로 저장합니다.
damo report heats는 주소 범위 × 시간 축의 히트맵을 터미널에 출력하여 핫/콜드 영역을 시각적으로 확인할 수 있습니다.
damo report wss는 워킹셋(실제 사용 중인 메모리) 크기의 분포를 보여줍니다.
damo schemes --action stat은 실제 회수 없이 매칭되는 리전의 크기만 확인하여 정책을 사전 검증할 수 있습니다.
실전 활용 사례
서버 환경: 대규모 메모리 최적화
# 시나리오: 256GB RAM 서버, memcached + MySQL 혼합 워크로드
# 목표: 사용되지 않는 memcached 캐시 페이지를 사전 회수
# DAMON_RECLAIM 설정 (물리 주소 전체 모니터링)
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 200000 > /sys/module/damon_reclaim/parameters/aggr_interval
echo 300 > /sys/module/damon_reclaim/parameters/min_age # 60초 콜드 기준
echo 50 > /sys/module/damon_reclaim/parameters/quota_ms
echo 512000000 > /sys/module/damon_reclaim/parameters/quota_sz # 512MB/주기
echo Y > /sys/module/damon_reclaim/parameters/enabled
모바일/임베디드 환경
# 시나리오: 8GB RAM 스마트폰, 메모리 제약 환경
# 목표: 백그라운드 앱의 콜드 페이지 적극 회수
# 짧은 집계 주기 + 낮은 콜드 기준
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 50000 > /sys/module/damon_reclaim/parameters/aggr_interval # 50ms
echo 100 > /sys/module/damon_reclaim/parameters/min_age # 5초 콜드
echo 5 > /sys/module/damon_reclaim/parameters/quota_ms # 낮은 쿼터
echo Y > /sys/module/damon_reclaim/parameters/enabled
컨테이너 환경: cgroup 필터
# 시나리오: Kubernetes Pod별 DAMOS 정책 분리
# sysfs 인터페이스로 memcg 필터 설정
# 특정 cgroup의 메모리만 회수 대상으로 지정
MEMCG_ID=$(cat /sys/fs/cgroup/my-pod/memory.current 2>/dev/null; \
cat /proc/cgroups | grep memory | awk '{print $2}')
# DAMOS 스킴에 memcg 필터 추가
echo 1 > .../schemes/0/filters/nr_filters
echo memcg > .../schemes/0/filters/0/type
echo "true" > .../schemes/0/filters/0/matching
echo ${MEMCG_ID} > .../schemes/0/filters/0/memcg_id
Redis/Memcached 인메모리 캐시 최적화
Redis나 Memcached 같은 인메모리 캐시(In-memory Cache) 서버는 대량의 데이터를 메모리에 적재하지만, 실제로는 상당 부분이 콜드 상태입니다. 만료된 키(Expired Key), 거의 조회되지 않는 값, TTL이 남았지만 접근이 없는 항목 등이 RSS를 불필요하게 증가시킵니다. DAMON으로 캐시 적중률(Hit Rate)에 영향 없이 콜드 페이지를 사전 회수할 수 있습니다.
# 시나리오: 64GB Redis 인스턴스, RSS 48GB 중 실제 핫 데이터 ~30GB
# 목표: 콜드 anonymous 페이지만 선별적으로 pageout하여 RSS 절감
# ── 1단계: DAMON_RECLAIM 파라미터 설정 ──
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval # 5ms
echo 200000 > /sys/module/damon_reclaim/parameters/aggr_interval # 200ms
echo 600 > /sys/module/damon_reclaim/parameters/min_age # 120초 콜드 기준
echo 30 > /sys/module/damon_reclaim/parameters/quota_ms # 주기당 최대 30ms
echo 256000000 > /sys/module/damon_reclaim/parameters/quota_sz # 주기당 256MB
# ── 2단계: anon 필터 (파일 캐시는 커널 페이지 캐시가 관리) ──
DAMON_CTX="/sys/kernel/mm/damon/admin/kdamonds/0/contexts/0"
echo 1 > ${DAMON_CTX}/schemes/0/filters/nr_filters
echo anon > ${DAMON_CTX}/schemes/0/filters/0/type
echo "true" > ${DAMON_CTX}/schemes/0/filters/0/matching # anon만 포함
echo Y > /sys/module/damon_reclaim/parameters/enabled
min_age를 120초 이상으로 보수적으로 설정해도 상당량의 콜드 페이지를 회수할 수 있습니다. 일반적으로 RSS 20~40% 절감, 캐시 적중률 변화 1% 미만을 기대할 수 있습니다.
# 검증: 캐시 성능 + RSS 변화 모니터링
redis-cli info stats | grep "keyspace_hits\|keyspace_misses"
ps -o rss= -p $(pidof redis-server) | awk '{print $1/1024"MB"}'
cat /sys/module/damon_reclaim/parameters/nr_reclaimed
Java/JVM 힙 메모리 최적화
JVM(Java Virtual Machine) 힙의 올드 제너레이션(Old/Tenured Generation)에는 장기 생존하지만 거의 접근되지 않는 콜드 객체가 상당수 존재합니다. DAMON의 vaddr 오퍼레이션으로 특정 JVM 프로세스를 모니터링하면, GC가 관리하지 못하는 페이지 수준의 콜드 영역을 식별하고 회수할 수 있습니다.
min_age를 GC 주기의 2~3배 이상으로 충분히 길게 설정해야 합니다.
# 시나리오: -Xmx32g JVM, Old Gen 24GB 중 활성 ~15GB
JAVA_PID=$(pidof java)
CTX="/sys/kernel/mm/damon/admin/kdamonds/0/contexts/0"
echo vaddr > ${CTX}/operations
echo 1 > ${CTX}/targets/nr_targets
echo ${JAVA_PID} > ${CTX}/targets/0/pid_target
# 보수적 파라미터 (GC 주기 고려)
echo 10000 > ${CTX}/monitoring_attrs/intervals/sample_us # 10ms
echo 500000 > ${CTX}/monitoring_attrs/intervals/aggr_us # 500ms
# 콜드 판정: 250초 이상 미접근 (GC Full 주기 대비 충분히 길게)
echo 1 > ${CTX}/schemes/nr_schemes
echo pageout > ${CTX}/schemes/0/action
echo 0 > ${CTX}/schemes/0/access_pattern/nr_accesses/max
echo 500 > ${CTX}/schemes/0/access_pattern/age/min
echo 20 > ${CTX}/schemes/0/quotas/ms
echo 128000000 > ${CTX}/schemes/0/quotas/bytes
jstat -gcutil로 GC 간격을 측정한 후 해당 간격의 2~3배로 설정하세요.
데이터베이스 버퍼 풀 최적화
MySQL(InnoDB)이나 PostgreSQL의 버퍼 풀(Buffer Pool)은 mmap()으로 매핑된 file-backed 페이지입니다. DAMON으로 콜드 버퍼 풀 페이지를 식별하여 다른 프로세스에 메모리를 환원할 수 있습니다.
fsync() 비용이 발생합니다. 데이터베이스 체크포인트 주기와 조율하여 더티 페이지 비율을 낮게 유지하세요.
# MySQL InnoDB buffer pool 32GB, file-backed 콜드 페이지만 회수
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 200000 > /sys/module/damon_reclaim/parameters/aggr_interval
echo 900 > /sys/module/damon_reclaim/parameters/min_age # 180초 콜드
echo 20 > /sys/module/damon_reclaim/parameters/quota_ms # 낮은 쿼터
# anon 필터 matching=false → file-backed 페이지만 타겟
DAMON_CTX="/sys/kernel/mm/damon/admin/kdamonds/0/contexts/0"
echo 1 > ${DAMON_CTX}/schemes/0/filters/nr_filters
echo anon > ${DAMON_CTX}/schemes/0/filters/0/type
echo "false" > ${DAMON_CTX}/schemes/0/filters/0/matching # anon 제외=file만
echo Y > /sys/module/damon_reclaim/parameters/enabled
# 버퍼 풀 히트율 모니터링
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
innodb_buffer_pool_dump_pct로 핫 페이지 목록을 유지하여 DAMON 회수 후에도 빠른 워밍을 보장합니다. PostgreSQL은 shared_buffers가 OS 페이지 캐시와 이중 캐싱되므로 file-backed 회수가 특히 효과적입니다. 초기에는 quota_ms를 10~20으로 보수적으로 시작하고, 히트율 99%+ 유지를 확인한 후 점진적으로 증가시키세요.
Meta TMO (Transparent Memory Offloading)
Meta(Facebook)의 TMO(Transparent Memory Offloading)는 DAMON을 프로덕션에 대규모로 적용한 대표적인 사례입니다. 수백만 대의 서버에서 운영되며, DAMON을 사용하여 콜드 메모리를 zswap으로 오프로딩(Offloading)합니다.
TMO 아키텍처
| 측정 항목 | TMO 없음 | TMO 적용 | 개선 |
|---|---|---|---|
| 서버당 DRAM 사용 | 100% | 70~80% | 20~30% 절약 |
| 워크로드 성능 (p99 지연(Latency)) | 기준 | ±2% 이내 | 성능 유지 |
| OOM 빈도 | 기준 | 50% 감소 | 안정성 향상 |
| CPU 오버헤드 | 0% | 0.5~1% | 미미 |
DAMON vs MGLRU
DAMON과 MGLRU(Multi-Gen LRU)는 모두 메모리 접근 패턴을 활용하지만, 접근 방식과 목적이 다릅니다. 두 메커니즘은 상호 보완적으로 동시에 사용할 수 있습니다.
| 특성 | DAMON | MGLRU |
|---|---|---|
| 도입 버전 | v5.15 | v6.1 |
| 주요 목적 | 데이터 접근 모니터링 + 자동 최적화 | LRU 알고리즘 근본 개선 |
| 동작 시점 | 항상 (프로액티브) | 메모리 회수(Memory Reclaim) 시 (리액티브) |
| 정밀도 | 리전 단위 (통계적 추정) | 페이지 단위 (정확) |
| 사용자 제어 | 세밀한 정책 정의 가능 | 제한적 (min_ttl_ms 등) |
| THP 최적화 | DAMOS_HUGEPAGE/NOHUGEPAGE | 내장 (folio 기반 에이징) |
| NUMA 지원 | MIGRATE_HOT/COLD (v6.9+) | lru_gen 내장 NUMA 밸런싱 |
| 동시 사용 | 가능 -- DAMON의 LRU_PRIO/DEPRIO가 MGLRU 세대 이동을 보완 | |
DAMON과 NUMA 티어링
커널 v6.9에서 도입된 DAMOS_MIGRATE_HOT/DAMOS_MIGRATE_COLD 액션은 DAMON의 접근 패턴 데이터를 NUMA 메모리 티어링에 활용합니다. CXL 메모리, HBM, PMEM 등 이기종 메모리 계층에서 데이터 배치를 최적화합니다.
# NUMA 티어링 예시: DRAM(Node 0) ↔ CXL(Node 1)
# 스킴 1: 콜드 페이지를 CXL로 디모션
echo migrate_cold > .../schemes/0/action
echo 0 > .../schemes/0/access_pattern/nr_accesses/min
echo 0 > .../schemes/0/access_pattern/nr_accesses/max
echo 100 > .../schemes/0/access_pattern/age/min
echo 1 > .../schemes/0/target_nid # CXL node
# 스킴 2: 핫 페이지를 DRAM으로 프로모션
echo migrate_hot > .../schemes/1/action
echo 15 > .../schemes/1/access_pattern/nr_accesses/min
echo 20 > .../schemes/1/access_pattern/nr_accesses/max
echo 0 > .../schemes/1/target_nid # DRAM node
ftrace/tracepoint 디버깅(Debugging)
DAMON은 ftrace tracepoint를 통해 내부 동작을 디버깅할 수 있습니다.
# 사용 가능한 DAMON tracepoint 확인
ls /sys/kernel/debug/tracing/events/damon/
# damon_aggregated -- 리전별 집계 결과
# tracepoint 활성화
echo 1 > /sys/kernel/debug/tracing/events/damon/damon_aggregated/enable
# 추적 시작
echo 1 > /sys/kernel/debug/tracing/tracing_on
# (DAMON 모니터링 실행 후)
cat /sys/kernel/debug/tracing/trace
# 출력 예시:
# kdamond.0-1234 [002] target_id=42 nr_regions=85
# region: start=0x7f0000000 end=0x7f0100000
# nr_accesses=18 age=0
# region: start=0x7f0100000 end=0x7f0800000
# nr_accesses=0 age=45
# 추적 중지
echo 0 > /sys/kernel/debug/tracing/tracing_on
# perf로 DAMON 이벤트 수집
perf record -e damon:damon_aggregated -a --duration 10
# BPF를 통한 DAMON 데이터 가공
bpftrace -e 'tracepoint:damon:damon_aggregated {
printf("region %lx-%lx: acc=%u age=%u\n",
args->start, args->end,
args->nr_accesses, args->age);
}'
cat /sys/kernel/mm/damon/admin/kdamonds/0/pid가 유효한 PID를 반환하는지 확인 (kdamond 실행 중)dmon_aggregatedtracepoint에 리전 데이터가 출력되는지 확인 (모니터링 동작 중)schemes/0/stats/nr_tried가 0이 아닌지 확인 (스킴 매칭이 발생하는지)schemes/0/stats/nr_applied가 0이면 쿼터/워터마크/필터 조건 확인
파라미터 튜닝 가이드
DAMON의 효과는 파라미터 설정에 크게 좌우됩니다. 워크로드 특성에 맞는 최적 파라미터를 찾는 것이 중요합니다.
| 파라미터 | 보수적 (저부하) | 기본값 (권장) | 공격적 (고정밀) | 튜닝 지침 |
|---|---|---|---|---|
sample_interval | 10ms | 5ms | 1ms | 짧을수록 정밀하나 CPU 사용 증가 |
aggr_interval | 200ms | 100ms | 50ms | 짧을수록 DAMOS 반응 빠름 |
update_interval | 120s | 60s | 10s | VMA 변경이 잦으면 짧게 |
min_nr_regions | 10 | 10 | 10 | 보통 기본값 유지 |
max_nr_regions | 100 | 1000 | 5000 | RSS 크기에 비례하여 조절 |
min_age (RECLAIM) | 500 | 200 | 50 | 워크로드의 재접근 주기 고려 |
quota_ms | 5 | 10 | 50 | CPU 여유에 맞게 |
체계적 튜닝 방법론
# Step 1: 기본값으로 워크로드 프로파일링
sudo damo record $(pidof my-app) --duration 60s -o baseline.json
# Step 2: 워킹셋 분포 확인
sudo damo report wss --input baseline.json
# → 워킹셋 대비 RSS가 2배 이상이면 DAMON_RECLAIM 효과적
# Step 3: DAMOS 시뮬레이션으로 회수 가능량 확인
sudo damo schemes --action stat \
--access_rate 0 0 \
--age 200 max \
$(pidof my-app)
# → stat 결과가 RSS의 20% 이상이면 회수 효과 기대
# Step 4: 보수적으로 시작하여 점진적 강화
echo 500 > /sys/module/damon_reclaim/parameters/min_age
echo 5 > /sys/module/damon_reclaim/parameters/quota_ms
echo Y > /sys/module/damon_reclaim/parameters/enabled
# Step 5: 모니터링하며 min_age, quota_ms 조절
watch -n 5 "cat /sys/module/damon_reclaim/parameters/nr_reclaimed; \
cat /proc/vmstat | grep -E 'pgscan|pgsteal'"
커널 빌드 옵션
# DAMON 관련 커널 설정 (make menuconfig)
# Memory Management options → Data Access Monitoring
CONFIG_DAMON=y # DAMON 코어 프레임워크
CONFIG_DAMON_VADDR=y # vaddr Operations Set
CONFIG_DAMON_PADDR=y # paddr Operations Set
CONFIG_DAMON_SYSFS=y # sysfs 인터페이스 (/sys/kernel/mm/damon/)
CONFIG_DAMON_DBGFS=n # debugfs 인터페이스 (레거시, 비권장)
CONFIG_DAMON_RECLAIM=y # DAMON 기반 프로액티브 회수
CONFIG_DAMON_LRU_SORT=y # DAMON 기반 LRU 정렬
# 의존성
CONFIG_MMU=y # 필수: MMU 지원
CONFIG_PAGE_IDLE_FLAG=y # paddr ops에 필요
# 선택적 (디버깅)
CONFIG_DAMON_KUNIT_TEST=n # DAMON 커널 유닛 테스트
| CONFIG 옵션 | 설명 | 기본값 | 프로덕션 권장 |
|---|---|---|---|
CONFIG_DAMON | 코어 프레임워크 | n | y |
CONFIG_DAMON_VADDR | 프로세스별 모니터링 | n | y |
CONFIG_DAMON_PADDR | 시스템 전체 모니터링 | n | y |
CONFIG_DAMON_SYSFS | sysfs 인터페이스 | n | y |
CONFIG_DAMON_RECLAIM | 프로액티브 회수 | n | y |
CONFIG_DAMON_LRU_SORT | LRU 재정렬 | n | 선택적 |
CONFIG_DAMON_DBGFS | debugfs (레거시) | n | n |
내부 자료 구조
DAMON의 핵심 동작을 이해하려면 kdamond 메인 루프의 실행 흐름을 추적해야 합니다.
/* mm/damon/core.c - kdamond 메인 루프 (대폭 단순화) */
static int kdamond_fn(void *data)
{
struct damon_ctx *ctx = data;
/* 리전 초기화 */
ctx->ops.init(ctx);
while (!kthread_should_stop() && ctx->kdamond) {
/* 워터마크 확인 -- 조건 불충족 시 슬립 */
if (kdamond_wait_activation(ctx))
continue;
/* Access Bit 클리어 (다음 체크 준비) */
ctx->ops.prepare_access_checks(ctx);
/* sample_interval 대기 */
kdamond_usleep(ctx->attrs.sample_interval);
/* Access Bit 확인 → nr_accesses 갱신 */
ctx->ops.check_accesses(ctx);
/* aggr_interval 도달 시 */
if (kdamond_aggregate_interval(ctx)) {
damon_merge_regions_of(ctx);
damon_do_apply_schemes(ctx); /* DAMOS 적용 */
kdamond_call_after_aggregation(ctx);
damon_reset_aggregated(ctx);
damon_split_regions_of(ctx);
}
/* update_interval 도달 시 */
if (kdamond_update_interval(ctx))
ctx->ops.update(ctx);
}
ctx->callback.before_terminate(ctx);
return 0;
}
설명
kdamond_fn은 kdamond 커널 스레드의 메인 함수입니다.
매 sample_interval마다 prepare_access_checks()로 Access Bit를 클리어하고, 다음 주기에 check_accesses()로 확인합니다.
aggr_interval에 도달하면 리전 병합, DAMOS 스킴 적용, 콜백 호출, 리전 분할을 순서대로 수행합니다.
update_interval에 도달하면 VMA 변경을 반영하여 리전을 재초기화합니다.
kdamond_wait_activation()은 워터마크 조건을 확인하여, 메모리가 충분하면 모니터링을 일시 중단합니다.
/* mm/damon/core.c - damon_split_regions_of() (단순화) */
static void damon_split_regions_of(
struct damon_ctx *ctx,
struct damon_target *t)
{
struct damon_region *r, *next;
unsigned int nr_new_regs = 0;
damon_for_each_region_safe(r, next, t) {
if (t->nr_regions >= ctx->attrs.max_nr_regions)
break;
/* 리전 크기가 2 * min_region_sz 이상이면 분할 */
if (damon_sz_region(r) > 2 * min_region_sz) {
struct damon_region *new;
unsigned long mid = r->ar.start + damon_sz_region(r) / 2;
new = damon_new_region(mid, r->ar.end);
new->nr_accesses = r->nr_accesses;
new->age = r->age;
r->ar.end = mid;
damon_insert_region(new, r, next, t);
nr_new_regs++;
}
}
}
설명
damon_split_regions_of()는 집계 주기(aggr_interval)마다 호출되어, 크기가 큰 리전을 절반으로 분할합니다.
분할 조건은 리전 크기가 2 * min_region_sz 이상이고, 전체 리전 수가 max_nr_regions 미만인 경우입니다.
분할 시 새 리전은 원본의 nr_accesses와 age 값을 상속받습니다.
이는 분할 직후에도 접근 패턴 정보가 유지되어 DAMOS 스킴 매칭이 올바르게 동작하기 위함입니다.
병합(merge)이 동질적인 리전을 합쳐 오버헤드를 줄이는 역할이라면, 분할(split)은 접근 패턴이 다른 영역을 세분화하여 정밀도를 높이는 역할을 합니다.
/* mm/damon/core.c - damon_do_apply_schemes() (단순화) */
static void damon_do_apply_schemes(
struct damon_ctx *ctx)
{
struct damos *s;
damon_for_each_scheme(s, ctx) {
struct damon_target *t;
/* 워터마크 확인 */
if (!damos_valid_wmarks(s))
continue;
/* 쿼터 리셋 확인 */
damos_check_quota_reset(s);
damon_for_each_target(t, ctx) {
struct damon_region *r;
damon_for_each_region(r, t) {
/* 패턴 매칭 */
if (!damos_access_pattern_match(s, r))
continue;
/* 필터 확인 */
if (damos_filter_out(s, t, r))
continue;
/* 쿼터 확인 */
if (damos_quota_exceeded(s))
break;
/* 액션 적용 */
sz = ctx->ops.apply_scheme(ctx, t, r, s);
s->stat.nr_applied++;
s->stat.sz_applied += sz;
}
}
}
}
설명
damon_do_apply_schemes()는 DAMOS의 핵심 실행 함수로, 등록된 모든 스킴을 순회하며 조건에 맞는 리전에 액션을 적용합니다.
실행 흐름은 4단계 필터링을 거칩니다:
- 워터마크 확인:
damos_valid_wmarks()가 현재 메모리 상태(빈 메모리 비율)를 워터마크와 비교합니다. 빈 메모리가high이상이면 스킴을 건너뛰고,low미만이면 적극 적용합니다. - 패턴 매칭:
damos_access_pattern_match()가 리전의nr_accesses,age, 크기를 스킴의 min/max 범위와 비교합니다. - 필터 확인:
damos_filter_out()이 anon/file, memcg, 주소 범위 등의 필터 조건을 평가하여 불필요한 페이지를 제외합니다. - 쿼터 확인:
damos_quota_exceeded()가 시간/크기 쿼터 초과 여부를 확인하여 과도한 자원 소비를 방지합니다.
ctx->ops.apply_scheme()은 Operations Set에 등록된 구현체를 호출하며, paddr의 경우 damon_pa_apply_scheme()이 pageout, lru_prio, stat 등의 액션을 수행합니다.
통계(nr_applied, sz_applied)는 sysfs의 stats/ 디렉토리를 통해 사용자 공간에서 확인할 수 있습니다.
damon_attrs 구조체(Struct)
/* include/linux/damon.h */
struct damon_attrs {
unsigned long sample_interval; /* 마이크로초 단위 */
unsigned long aggr_interval;
unsigned long ops_update_interval;
unsigned long min_nr_regions;
unsigned long max_nr_regions;
};
damos_watermarks 구조체
/* include/linux/damon.h - 워터마크 */
struct damos_watermarks {
enum damos_wmark_metric metric; /* DAMOS_WMARK_FREE_MEM_RATE */
unsigned long interval; /* 확인 주기 (마이크로초) */
unsigned long high; /* 높은 워터마크 (퍼밀, 1/1000) */
unsigned long mid; /* 중간 워터마크 */
unsigned long low; /* 낮은 워터마크 */
};
설명
워터마크는 퍼밀(permil, 1/1000) 단위로 설정합니다.high=500이면 빈 메모리가 전체의 50% 이상일 때 DAMOS 스킴을 비활성화합니다.
low=200이면 빈 메모리가 20% 미만일 때 적극적으로 스킴을 적용합니다.
mid는 현재 상태를 유지하는 구간입니다 (히스테리시스).
성능 영향 분석
| 측정 항목 | DAMON 비활성 | DAMON 기본값 | DAMON + RECLAIM | 비고 |
|---|---|---|---|---|
| CPU 오버헤드 | 0% | 0.5~0.8% | 0.8~1.2% | 워크로드/RSS 크기에 따라 변동 |
| 메모리 사용 | 0 | ~200KB | ~300KB | 리전 메타데이터 + 스킴 구조체 |
| TLB miss 증가 | 0% | <0.1% | <0.5% | Access Bit 클리어에 의한 추가 TLB miss |
| 메모리 절약 | - | 모니터링만 | 10~30% RSS 감소 | 콜드 페이지 회수 효과 |
sample_interval이 매우 짧으면(1ms 미만) TLB thrashing이 발생하여 오히려 성능이 저하될 수 있습니다. 프로덕션에서는 반드시 벤치마크를 통해 최적 값을 확인하세요.
커널 내부 DAMON API
커널 모듈(Kernel Module)에서 DAMON을 프로그래밍적으로 사용하는 주요 API입니다.
/* mm/damon/core.c - 주요 API */
/* DAMON context 생성/해제 */
struct damon_ctx *damon_new_ctx(void);
void damon_destroy_ctx(struct damon_ctx *ctx);
/* Operations Set 등록 */
int damon_select_ops(struct damon_ctx *ctx,
enum damon_ops_id id);
/* 모니터링 대상 추가 */
struct damon_target *damon_new_target(void);
void damon_add_target(struct damon_ctx *ctx,
struct damon_target *t);
/* 리전 추가 */
struct damon_region *damon_new_region(
unsigned long start, unsigned long end);
void damon_add_region(struct damon_region *r,
struct damon_target *t);
/* DAMOS 스킴 추가 */
struct damos *damon_new_scheme(
struct damos_access_pattern *pattern,
enum damos_action action,
unsigned long apply_interval_us,
struct damos_quota *quota,
struct damos_watermarks *wmarks);
void damon_set_schemes(struct damon_ctx *ctx,
struct damos **schemes,
ssize_t nr_schemes);
/* 모니터링 시작/중지 */
int damon_start(struct damon_ctx **ctxs,
int nr_ctxs, bool exclusive);
int damon_stop(struct damon_ctx **ctxs, int nr_ctxs);
설명
커널 모듈에서 DAMON을 사용하려면damon_new_ctx()로 컨텍스트를 생성하고, damon_select_ops()로 Operations Set을 선택한 뒤, damon_add_target()으로 모니터링 대상을 추가합니다.
DAMOS 스킴을 설정하려면 damon_new_scheme()으로 스킴을 생성하여 damon_set_schemes()로 등록합니다.
damon_start()를 호출하면 kdamond 커널 스레드가 생성되어 모니터링을 시작합니다.
DAMON_RECLAIM과 DAMON_LRU_SORT가 이 API의 대표적인 사용 사례입니다.
안티패턴과 주의사항
안티패턴 1: 과도한 샘플링
# 잘못된 설정: sample_interval이 너무 짧음
echo 100 > .../monitoring_attrs/intervals/sample_us # 0.1ms!
echo 5000 > .../nr_regions/max # 5000 리전!
# → CPU 오버헤드 5%+, TLB thrashing 발생
# 올바른 설정: 기본값 또는 보수적 설정
echo 5000 > .../monitoring_attrs/intervals/sample_us # 5ms (기본값)
echo 1000 > .../nr_regions/max # 1000 리전 (기본값)
안티패턴 2: 쿼터 없는 DAMOS
# 잘못된 설정: 쿼터 없이 pageout 스킴 활성화
echo pageout > .../schemes/0/action
echo 0 > .../schemes/0/quotas/ms # 무제한 시간!
echo 0 > .../schemes/0/quotas/bytes # 무제한 크기!
# → 한 번에 수 GB를 회수하여 I/O 폭주
# 올바른 설정: 항상 쿼터 설정
echo 10 > .../schemes/0/quotas/ms # 10ms/주기
echo 128000000 > .../schemes/0/quotas/bytes # 128MB/주기
안티패턴 3: min_age가 너무 낮은 RECLAIM
# 잘못된 설정: 1초만 미접근이면 회수
echo 10 > /sys/module/damon_reclaim/parameters/min_age
# → 버스트 워크로드에서 곧 재접근될 페이지까지 회수
# → page fault 증가 → 성능 저하
# 올바른 설정: 워크로드의 재접근 패턴 고려
echo 200 > /sys/module/damon_reclaim/parameters/min_age # 20초 (기본값)
DAMON vs 기존 메커니즘 종합 비교
| 특성 | DAMON | idle page tracking | perf mem | /proc/PID/pagemap |
|---|---|---|---|---|
| 커널 내장 | v5.15+ | v4.3+ | v2.6.31+ | v2.6.25+ |
| 자동 액션 | DAMOS | 없음 | 없음 | 없음 |
| 오버헤드 | <1% CPU | ~5% (풀스캔) | 1~3% (PMU 샘플링) | 높음 (PT 순회) |
| 정밀도 | 리전 단위 | 페이지 단위 | 주소 단위 (PMU) | 페이지 단위 |
| 하드웨어 의존 | PTE Access Bit | PTE Idle Bit | PMU (IBS/PEBS) | PTE |
| 실시간 대응 | 가능 (DAMOS) | 불가 (폴링) | 불가 (오프라인) | 불가 (폴링) |
| NUMA 최적화 | v6.9+ | 불가 | 가능 (분석) | 불가 |
자동 쿼터 튜닝 (DAMOS Quota Goal)
커널 v6.8에서 도입된 DAMOS Quota Goal은 사용자가 명시적 쿼터 값을 설정하는 대신, 목표 메트릭을 지정하면 DAMON이 자동으로 쿼터를 조절하는 피드백 루프입니다.
/* include/linux/damon.h - 쿼터 목표 구조체 */
struct damos_quota_goal {
enum damos_quota_goal_metric metric;
/* DAMOS_QUOTA_USER_INPUT: 사용자 직접 입력 */
/* DAMOS_QUOTA_SOME_MEM_PSI_US: PSI some 값 */
unsigned long target_value; /* 목표 값 */
unsigned long current_value; /* 현재 값 (커널이 갱신) */
struct list_head list;
};
설명
DAMOS_QUOTA_SOME_MEM_PSI_US는 메모리 PSI(Pressure Stall Information)의 some 값(마이크로초)을 목표로 합니다.
예를 들어 target_value를 10000(10ms)으로 설정하면, DAMON은 PSI some이 10ms를 넘지 않도록 쿼터를 자동 조절합니다.
PSI가 높아지면(메모리 압박 증가) 쿼터를 늘려 더 많이 회수하고, PSI가 낮아지면 쿼터를 줄여 불필요한 회수를 방지합니다.
자동 튜닝 알고리즘
DAMON의 자동 쿼터 튜닝은 비례 제어(proportional control) 기반으로 동작합니다:
/* mm/damon/core.c - 쿼터 자동 조절 (단순화) */
static void damos_adjust_quota(
struct damos *s)
{
struct damos_quota *q = &s->quota;
struct damos_quota_goal *g;
unsigned long score;
list_for_each_entry(g, &q->goals, list) {
/* 현재 메트릭 수집 */
damos_quota_goal_update(g);
/* 목표 대비 비율 계산 */
if (g->current_value < g->target_value) {
/* 여유 있음 → 쿼터 축소 */
score = g->current_value * 10000 / g->target_value;
} else {
/* 압박 상태 → 쿼터 확대 */
score = g->current_value * 10000 / g->target_value;
}
}
/* 유효 쿼터 조절 (최소/최대 제한 적용) */
q->esz = q->esz * score / 10000;
q->esz = clamp(q->esz, DAMOS_QUOTA_MIN, q->sz);
}
# sysfs에서 자동 쿼터 튜닝 설정
# PSI some 5ms 이하를 목표로 자동 조절
echo 1 > .../schemes/0/quotas/goals/nr_goals
echo some_mem_psi_us > .../schemes/0/quotas/goals/0/metric
echo 5000 > .../schemes/0/quotas/goals/0/target_value
# 초기 쿼터는 보수적으로 설정 (자동 튜닝이 조절)
echo 5 > .../schemes/0/quotas/ms
echo 64000000 > .../schemes/0/quotas/bytes
접근 패턴 분석 기법
DAMON의 모니터링 데이터를 효과적으로 분석하면 워크로드의 메모리 사용 특성을 정밀하게 파악할 수 있습니다.
워킹셋 크기 분석
워킹셋(Working Set Size)은 특정 시간 동안 실제로 접근된 메모리 양입니다. DAMON의 nr_accesses > 0인 리전의 크기 합이 워킹셋의 근사치입니다.
# damo로 워킹셋 분석
sudo damo record $(pidof redis-server) --duration 120s -o redis.json
# 시간대별 워킹셋 변화 확인
sudo damo report wss --input redis.json --sortby time
# 워킹셋 분포 히스토그램
sudo damo report wss --input redis.json --percentiles 10 25 50 75 90 95 99
# 출력 예시:
# percentile wss
# 10 128MB (최소 활성)
# 25 256MB (1사분위)
# 50 512MB (중앙값)
# 75 1.2GB (3사분위)
# 90 1.8GB (p90)
# 95 2.1GB (p95)
# 99 2.4GB (p99)
# → RSS 4GB 중 약 50%가 일상적으로 사용되지 않음
# → DAMON_RECLAIM으로 ~2GB 회수 가능
핫/콜드 메모리 분류
| 분류 | nr_accesses 범위 | 비율 (일반적) | 최적 처리 |
|---|---|---|---|
| 핫 (Hot) | aggr/sample의 80%+ | 10~30% | THP 프로모션, DRAM 고정 |
| 웜 (Warm) | aggr/sample의 20~80% | 20~40% | LRU active 유지 |
| 쿨 (Cool) | aggr/sample의 1~20% | 10~20% | LRU inactive 이동 |
| 콜드 (Cold) | 0 (age > threshold) | 20~50% | 프로액티브 회수, CXL 디모션 |
# 핫/콜드 분포를 bpftrace로 실시간 확인
bpftrace -e '
tracepoint:damon:damon_aggregated {
@hot = count(args->nr_accesses >= 15);
@warm = count(args->nr_accesses >= 5 && args->nr_accesses < 15);
@cool = count(args->nr_accesses >= 1 && args->nr_accesses < 5);
@cold = count(args->nr_accesses == 0);
@total_cold_bytes = sum(args->nr_accesses == 0 ?
(args->end - args->start) : 0);
}
interval:s:10 {
printf("hot=%d warm=%d cool=%d cold=%d cold_bytes=%lld\n",
@hot, @warm, @cool, @cold, @total_cold_bytes);
clear(@hot); clear(@warm); clear(@cool); clear(@cold);
clear(@total_cold_bytes);
}'
접근 빈도 히스토그램
# DAMON 리전 데이터를 사용한 접근 빈도 히스토그램 생성
bpftrace -e '
tracepoint:damon:damon_aggregated {
@accesses = lhist(args->nr_accesses, 0, 20, 1);
@sizes[args->nr_accesses] =
sum(args->end - args->start);
}
END {
printf("\n--- 접근 빈도별 메모리 크기 분포 ---\n");
print(@sizes);
}'
프로덕션 운영 플레이북
단계 1: 프로파일링 (관측만)
# 1주일간 워크로드 프로파일링
# stat 액션으로 실제 회수 없이 데이터만 수집
# sysfs 설정
echo 1 > /sys/kernel/mm/damon/admin/kdamonds/nr_kdamonds
echo 1 > .../kdamonds/0/contexts/nr_contexts
echo paddr > .../kdamonds/0/contexts/0/operations
# 모니터링 파라미터
echo 5000 > .../contexts/0/monitoring_attrs/intervals/sample_us
echo 100000 > .../contexts/0/monitoring_attrs/intervals/aggr_us
echo 60000000 > .../contexts/0/monitoring_attrs/intervals/update_us
# stat 스킴 (콜드 페이지 크기만 측정)
echo 1 > .../contexts/0/schemes/nr_schemes
echo stat > .../contexts/0/schemes/0/action
echo 0 > .../contexts/0/schemes/0/access_pattern/nr_accesses/min
echo 0 > .../contexts/0/schemes/0/access_pattern/nr_accesses/max
echo 200 > .../contexts/0/schemes/0/access_pattern/age/min
# 시작
echo commit > .../kdamonds/0/state
echo on > .../kdamonds/0/state
# 매일 통계 수집 (cron에 등록)
echo update_schemes_stats > .../kdamonds/0/state
DATE=$(date +%Y%m%d)
echo "$DATE nr_tried=$(cat .../schemes/0/stats/nr_tried) \
sz_tried=$(cat .../schemes/0/stats/sz_tried)" >> /var/log/damon-stats.log
단계 2: 스테이징 환경 테스트
# 프로파일링 결과 기반으로 보수적 DAMOS 적용
# DAMON_RECLAIM으로 간편 시작
echo 300 > /sys/module/damon_reclaim/parameters/min_age # 30초 콜드
echo 5 > /sys/module/damon_reclaim/parameters/quota_ms # 5ms (보수적)
echo 64000000 > /sys/module/damon_reclaim/parameters/quota_sz # 64MB
echo 500 > /sys/module/damon_reclaim/parameters/wmarks_high # 50%
echo 400 > /sys/module/damon_reclaim/parameters/wmarks_mid # 40%
echo 200 > /sys/module/damon_reclaim/parameters/wmarks_low # 20%
echo Y > /sys/module/damon_reclaim/parameters/enabled
# 성능 지표 모니터링 (A/B 비교)
while true; do
echo "$(date) \
reclaimed=$(cat /sys/module/damon_reclaim/parameters/nr_reclaimed) \
pgscan=$(grep pgscan_direct /proc/vmstat | awk '{print $2}') \
pgfault=$(grep pgfault /proc/vmstat | awk '{print $2}') \
psi_mem=$(cat /proc/pressure/memory | head -1)"
sleep 60
done >> /var/log/damon-perf.log
단계 3: 프로덕션 점진적 롤아웃
# 카나리 배포: 전체 호스트의 5%에 먼저 적용
# 1주일 모니터링 후 문제없으면 25% → 50% → 100%
# 프로덕션 최종 설정 (예시)
echo 200 > /sys/module/damon_reclaim/parameters/min_age
echo 10 > /sys/module/damon_reclaim/parameters/quota_ms
echo 128000000 > /sys/module/damon_reclaim/parameters/quota_sz
# 자동 쿼터 튜닝 활성화 (v6.8+)
# → PSI 기반으로 자동 조절하므로 수동 튜닝 부담 감소
# 롤백 절차
echo N > /sys/module/damon_reclaim/parameters/enabled
# → 즉시 회수 중단, 기존 페이지에 영향 없음
모니터링 알림 설정
# Prometheus + Grafana 연동 (node_exporter custom collector)
# /var/lib/node_exporter/textfile/damon.prom
cat <<'EOF' > /usr/local/bin/damon-metrics.sh
#!/bin/bash
METRICS=/var/lib/node_exporter/textfile/damon.prom
{
echo "# HELP damon_reclaim_nr_reclaimed Total pages reclaimed by DAMON"
echo "# TYPE damon_reclaim_nr_reclaimed counter"
echo "damon_reclaim_nr_reclaimed $(cat /sys/module/damon_reclaim/parameters/nr_reclaimed 2>/dev/null || echo 0)"
echo "# HELP damon_reclaim_bytes_reclaimed Total bytes reclaimed"
echo "# TYPE damon_reclaim_bytes_reclaimed counter"
echo "damon_reclaim_bytes_reclaimed $(cat /sys/module/damon_reclaim/parameters/bytes_reclaimed 2>/dev/null || echo 0)"
} > ${METRICS}.tmp
mv ${METRICS}.tmp ${METRICS}
EOF
chmod +x /usr/local/bin/damon-metrics.sh
# cron: 1분마다 메트릭 수집
echo "* * * * * root /usr/local/bin/damon-metrics.sh" > /etc/cron.d/damon-metrics
- DAMON 활성화 전후 애플리케이션 레이턴시(p50/p99) 비교
- page fault 비율(
/proc/vmstatpgfault, pgmajfault) 증가 여부 확인 - PSI 메모리(
/proc/pressure/memory) 악화 여부 모니터링 - 스왑 사용량 급증 여부 확인 (DAMOS pageout → 스왑 I/O)
- 워터마크 설정이 환경에 적합한지 (충분한 히스테리시스)
- 롤백(Rollback) 자동화 스크립트 준비 (알림 → 자동 비활성화)
Android/ChromeOS에서의 DAMON
DAMON은 모바일/ChromeOS 환경에서 특히 효과적입니다. 제한된 메모리에서 멀티태스킹 성능을 최적화하기 위해 다음과 같이 활용됩니다.
Android LMKD 대체/보완
| 특성 | Android LMKD | DAMON + DAMOS |
|---|---|---|
| 메모리 회수 방식 | 프로세스 킬 (전체 메모리 반환) | 콜드 페이지만 선택적 회수 |
| 사용자 경험 | 앱 재시작(Reboot) 필요 | 앱 유지, 백그라운드 메모리만 회수 |
| 접근 패턴 활용 | 없음 (RSS/oom_score만) | 실제 접근 빈도 기반 |
| 오버헤드 | 킬 시 순간 비용 | 상시 ~0.5% CPU |
| 적합한 상황 | 심각한 메모리 부족 | 일상적 메모리 최적화 |
# Android 환경 DAMON 설정 예시
# 백그라운드 앱의 콜드 페이지를 zram으로 스왑
# 짧은 주기, 공격적 콜드 기준
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 50000 > /sys/module/damon_reclaim/parameters/aggr_interval # 50ms
echo 60 > /sys/module/damon_reclaim/parameters/min_age # 3초 콜드
echo 3 > /sys/module/damon_reclaim/parameters/quota_ms # 낮은 CPU 쿼터
echo 32000000 > /sys/module/damon_reclaim/parameters/quota_sz # 32MB/주기
# 프리 메모리 15% 이하에서만 동작
echo 200 > /sys/module/damon_reclaim/parameters/wmarks_high # 20%
echo 150 > /sys/module/damon_reclaim/parameters/wmarks_mid # 15%
echo 100 > /sys/module/damon_reclaim/parameters/wmarks_low # 10%
echo Y > /sys/module/damon_reclaim/parameters/enabled
ChromeOS 메모리 관리
Google의 ChromeOS는 DAMON을 탭 관리에 활용합니다. 백그라운드 탭의 렌더러 프로세스에 대해 DAMON으로 접근 패턴을 모니터링하고, 완전히 콜드한 탭의 메모리를 사전 회수하여 활성 탭의 성능을 보장합니다.
DAMON vs Idle Page Tracking 상세 비교
idle page tracking(/sys/kernel/mm/page_idle/bitmap)은 커널 v4.3에서 도입된 페이지 단위 접근 추적 메커니즘입니다. DAMON과 유사한 목적이지만 구현 방식과 성능 특성이 크게 다릅니다.
# idle page tracking 사용법 (비교용)
# 1. 모든 페이지를 idle로 마킹
echo 1 > /sys/kernel/mm/page_idle/bitmap # 전체 페이지 idle 설정
# 2. 일정 시간 대기 (워크로드 실행)
sleep 60
# 3. 여전히 idle인 페이지 확인
python3 -c "
import struct, os
with open('/sys/kernel/mm/page_idle/bitmap', 'rb') as f:
idle_count = 0
total_count = 0
while True:
data = f.read(8)
if not data: break
val = struct.unpack('Q', data)[0]
for i in range(64):
total_count += 1
if val & (1 << i):
idle_count += 1
print(f'Idle pages: {idle_count}/{total_count} '
f'({idle_count*4/1024:.0f}MB / {total_count*4/1024:.0f}MB)')
"
| 비교 항목 | DAMON | idle page tracking |
|---|---|---|
| 샘플링 단위 | 리전 (랜덤 1 페이지) | 모든 페이지 (비트맵) |
| 자동화 | 커널 내부 자동 루프 | 유저스페이스 폴링 필요 |
| 오버헤드 (1GB RSS) | ~100 PTE 확인/주기 | ~256K PTE 확인/주기 |
| 접근 빈도 정보 | nr_accesses (다단계) | idle/non-idle (이진) |
| 자동 액션 | DAMOS (pageout, hugepage 등) | 없음 (외부 도구 필요) |
| 필터링 | anon/memcg/addr/young/target | 없음 |
| NUMA 티어링 | MIGRATE_HOT/COLD | 없음 |
BPF와 DAMON 통합
BPF(Berkeley Packet Filter)를 활용하면 DAMON의 tracepoint 데이터를 실시간으로 가공하거나, 커스텀 정책 로직을 구현할 수 있습니다.
/* BPF 프로그램: DAMON 데이터 기반 커스텀 히트맵 */
/* damon_heatmap.bpf.c */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10000);
__type(key, __u64); /* 리전 시작 주소 (4KB 정렬) */
__type(value, __u32); /* 누적 접근 횟수 */
} access_map SEC(".maps");
SEC("tracepoint/damon/damon_aggregated")
int handle_aggregated(struct trace_event_raw_damon_aggregated *ctx)
{
__u64 start = ctx->start;
__u32 accesses = ctx->nr_accesses;
__u32 *val;
/* 4KB 정렬 키로 변환 */
start &= ~(0xFFFULL);
val = bpf_map_lookup_elem(&access_map, &start);
if (val) {
*val += accesses;
} else {
bpf_map_update_elem(&access_map, &start,
&accesses, BPF_ANY);
}
return 0;
}
설명
이 BPF 프로그램은damon_aggregated tracepoint에 attach되어, 각 리전의 접근 횟수를 BPF 해시(Hash)맵에 누적합니다.
유저스페이스 도구가 이 맵을 주기적으로 읽어 히트맵을 생성하거나, 특정 임계값 초과 시 알림을 발생시킬 수 있습니다.
향후 DAMON에 BPF 기반 커스텀 스킴 액션이 추가되면, DAMOS의 기본 액션(pageout 등)을 넘어선 유연한 정책 구현이 가능해질 전망입니다.
# BPF 프로그램 로드 및 히트맵 출력
sudo bpftool prog load damon_heatmap.bpf.o /sys/fs/bpf/damon_heatmap
sudo bpftool prog attach pinned /sys/fs/bpf/damon_heatmap tracepoint damon damon_aggregated
# 10초 후 맵 덤프
sleep 10
sudo bpftool map dump pinned /sys/fs/bpf/access_map | \
sort -t: -k2 -n -r | head -20
# → 가장 핫한 주소 영역 Top 20
트러블슈팅 가이드
DAMON이 동작하지 않는 경우
# 1. 커널 설정 확인
zcat /proc/config.gz | grep DAMON
# CONFIG_DAMON=y 확인, 없으면 커널 재빌드 필요
# 2. sysfs 존재 확인
ls /sys/kernel/mm/damon/admin/
# 없으면 CONFIG_DAMON_SYSFS=y 필요
# 3. kdamond 상태 확인
cat /sys/kernel/mm/damon/admin/kdamonds/0/state
# "on"이 아니면 시작되지 않음
cat /sys/kernel/mm/damon/admin/kdamonds/0/pid
# PID가 없으면 kdamond가 실행 중이 아님
# 4. commit 누락 확인 (가장 흔한 실수)
echo commit > /sys/kernel/mm/damon/admin/kdamonds/0/state
# → sysfs 설정 변경 후 반드시 commit 필요!
# 5. 대상 PID 유효성 확인
cat .../contexts/0/targets/0/pid_target
ls /proc/$(cat .../contexts/0/targets/0/pid_target)
# 프로세스가 이미 종료되었으면 모니터링 불가
DAMOS가 회수하지 않는 경우
# 1. 스킴 통계 확인
echo update_schemes_stats > .../kdamonds/0/state
echo "nr_tried=$(cat .../schemes/0/stats/nr_tried)"
echo "sz_tried=$(cat .../schemes/0/stats/sz_tried)"
echo "nr_applied=$(cat .../schemes/0/stats/nr_applied)"
echo "sz_applied=$(cat .../schemes/0/stats/sz_applied)"
echo "qt_exceeds=$(cat .../schemes/0/stats/qt_exceeds)"
# nr_tried=0: 패턴 매칭 실패 → access_pattern 조건 완화
# nr_tried>0, nr_applied=0: 필터/쿼터/워터마크 차단
# qt_exceeds>0: 쿼터 초과 → quota_ms/quota_sz 증가
# 2. 워터마크 확인 (free memory)
free -m
# free가 wmarks_high 이상이면 스킴 비활성화 상태
# 3. min_age 확인
# min_age=200, aggr_interval=100ms → 20초 이상 미접근 필요
# 워크로드가 20초 내에 모든 메모리를 재접근하면 콜드 리전 없음
# 4. 필터 확인
cat .../schemes/0/filters/0/type
cat .../schemes/0/filters/0/matching
# anon 필터가 matching=true이면 file-backed 페이지 제외
오버헤드가 높은 경우
# kdamond CPU 사용률 확인
top -p $(cat /sys/kernel/mm/damon/admin/kdamonds/0/pid)
# CPU 1% 이상이면 파라미터 완화
echo 10000 > .../monitoring_attrs/intervals/sample_us # 5ms → 10ms
echo 500 > .../monitoring_attrs/nr_regions/max # 1000 → 500
echo commit > .../kdamonds/0/state
# TLB miss 증가 확인
perf stat -e dTLB-load-misses,dTLB-store-misses -a sleep 10
# DAMON 활성화 전후 비교
systemd 서비스 연동
DAMON_RECLAIM을 시스템 부팅 시 자동으로 활성화하려면 systemd 서비스 유닛을 작성합니다.
# /etc/systemd/system/damon-reclaim.service
[Unit]
Description=DAMON Proactive Memory Reclaim
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c '\
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval; \
echo 100000 > /sys/module/damon_reclaim/parameters/aggr_interval; \
echo 200 > /sys/module/damon_reclaim/parameters/min_age; \
echo 10 > /sys/module/damon_reclaim/parameters/quota_ms; \
echo 128000000 > /sys/module/damon_reclaim/parameters/quota_sz; \
echo 500 > /sys/module/damon_reclaim/parameters/wmarks_high; \
echo 400 > /sys/module/damon_reclaim/parameters/wmarks_mid; \
echo 200 > /sys/module/damon_reclaim/parameters/wmarks_low; \
echo Y > /sys/module/damon_reclaim/parameters/enabled'
ExecStop=/bin/bash -c '\
echo N > /sys/module/damon_reclaim/parameters/enabled'
[Install]
WantedBy=multi-user.target
# 서비스 등록 및 시작
sudo systemctl daemon-reload
sudo systemctl enable damon-reclaim.service
sudo systemctl start damon-reclaim.service
# 상태 확인
sudo systemctl status damon-reclaim.service
cat /sys/module/damon_reclaim/parameters/enabled # Y
cat /sys/module/damon_reclaim/parameters/nr_reclaimed
damon_reclaim.enabled=Y damon_reclaim.min_age=200 damon_reclaim.quota_ms=10
버전별 주요 변경사항
| 커널 버전 | 변경사항 | 관련 CONFIG |
|---|---|---|
| v5.15 | DAMON 코어 프레임워크 최초 머지, debugfs 인터페이스, vaddr/paddr ops | CONFIG_DAMON |
| v5.16 | DAMOS(Data Access-aware Memory Operation Schemes) 도입 | CONFIG_DAMON |
| v5.17 | DAMON_RECLAIM 모듈, 워터마크 기반 프로액티브 회수 | CONFIG_DAMON_RECLAIM |
| v5.18 | sysfs 인터페이스(/sys/kernel/mm/damon/admin/), fvaddr ops | CONFIG_DAMON_SYSFS |
| v5.19 | DAMOS 쿼터 시스템 도입 | - |
| v6.0 | DAMON_LRU_SORT, LRU_PRIO/LRU_DEPRIO 액션 | CONFIG_DAMON_LRU_SORT |
| v6.2 | DAMOS 워터마크 자동 비활성화 개선 | - |
| v6.3 | DAMOS 필터 프레임워크 (anon, memcg, addr) | - |
| v6.5 | young 필터, target 필터 추가 | - |
| v6.7 | DAMOS apply interval 설정 | - |
| v6.8 | 자동 쿼터 튜닝 (DAMOS Quota Goal), PSI 기반 | - |
| v6.9 | MIGRATE_HOT/MIGRATE_COLD 액션, NUMA 티어링 | - |
| v6.10+ | 다중 kdamond 개선, BPF 연동 논의 | - |
DAMON 발전 방향
DAMON은 활발하게 발전 중인 서브시스템으로, 다음과 같은 방향으로 개선이 진행 중입니다:
- 자동 쿼터 튜닝 (v6.8+) -- PSI 메트릭 기반 피드백 루프로 쿼터를 자동 조절합니다.
- BPF 통합 -- BPF 프로그램으로 DAMON 콜백을 처리하여 더 유연한 정책을 구현합니다.
- CXL/PMEM 티어링 고도화 -- MIGRATE_HOT/COLD 액션과 memory tiering daemon 연동.
- DAMOS 필터 확장 -- 파일 시스템, 워크로드 타입 등 더 세밀한 필터 조건.
- 다중 kdamond 지원 -- NUMA 노드별 독립적인 kdamond로 확장성 개선.
- folio 통합 -- 대형 folio(large folio) 기반 모니터링으로 THP 최적화.
참고자료
커널 문서
- DAMON Admin Guide -- DAMON 관리자 가이드입니다
- DAMON Design Documentation -- DAMON 설계 문서입니다
- DAMON Usage -- DAMON 사용법을 설명합니다
LWN 기사
- DAMON: data access monitoring framework (2021) -- DAMON 프레임워크를 처음 소개하는 기사입니다
- An update on DAMON (2021) -- DAMON의 발전 현황을 업데이트합니다
- DAMOS: operation schemes (2023) -- DAMON 기반 오퍼레이션 스킴을 설명합니다
- Improving DAMON (2024) -- DAMON 개선 방향을 다룹니다
커널 소스
- mm/damon/core.c -- DAMON 핵심 구현 코드입니다
- mm/damon/vaddr.c -- 가상 주소 공간 모니터링 구현입니다
- mm/damon/paddr.c -- 물리 주소 공간 모니터링 구현입니다
- mm/damon/sysfs.c -- DAMON sysfs 인터페이스 구현입니다
발표 자료
- SeongJae Park, "DAMON: Data Access MONitoring Framework" (Linux Plumbers Conference 2021) -- DAMON의 설계와 활용 사례를 발표한 자료입니다