커널의 메모리 재할당
1. /proc/smaps — 프로세스 메모리 상세 분석
/proc/[pid]/maps의 확장판으로, 각 VMA(Virtual Memory Area)별 상세 메모리 통계 제공
핵심 필드
필드 의미
| Size | 가상 주소 공간 크기 (top의 VIRT) |
| Rss | 실제 물리 메모리 점유량 (top의 RES) |
| Pss | 공유 비율 보정된 실질 메모리 비용 |
| Shared_Clean | 공유 라이브러리 코드 — 언제든 해제 가능 |
| Private_Dirty | 프로세스가 실제로 소유한 메모리 (힙, 스택, CoW 후 수정) |
| Swap | 스왑으로 내보내진 페이지 수 |
| Anonymous | 파일 백킹 없는 익명 메모리 (malloc, 스택 등) |
PSS가 중요한 이유
4개 프로세스가 400KB 공유 라이브러리 사용 시
RSS: 각 400KB → 합계 1600KB (과대계상)
PSS: 각 100KB → 합계 400KB (현실 반영)
실용 명령
# PSS 합산 (실질 메모리 사용량)
grep Pss /proc/$(pgrep nginx)/smaps | awk '{sum+=$2} END {print sum" kB"}'
# Private_Dirty 합산
grep Private_Dirty /proc/$(pgrep python3)/smaps | awk '{sum+=$2} END {print sum" kB"}'
# 전체 요약
cat /proc/$(pgrep bash)/smaps_rollup
2. 버디 시스템 (Buddy System)
커널이 물리 메모리의 외부 단편화를 방지하기 위해 사용하는 페이지 할당 메커니즘
구조
메모리를 2의 거듭제곱 크기 블록으로 관리, order별 free list 유지
order 0: 4KB
order 1: 8KB
order 2: 16KB
...
order 10: 4MB (x86 최대)
order 11: 8MB (Raspberry Pi — ARM은 더 큰 블록 지원)
cat /proc/buddyinfo
# Node 0, zone DMA 36 160 96 40 28 18 8 7 5 3 2 9
# ^ ^ ^
# order0 1 2 ...
할당 과정
12KB 요청 → 16KB(order 2) 필요
order 2 free 블록 있음? → 즉시 할당
없으면 → order 3(32KB)을 절반으로 분할
16KB 할당 + 나머지 16KB는 버디로 free list에 반환
해제 및 합치기 (Coalescing) ← 핵심
16KB 블록 해제
→ 버디도 free? → 32KB로 합침
→ 그 버디도 free? → 64KB로 합침
→ 반복 → 큰 연속 블록 복원
버디 주소 계산: 버디 주소 = 블록 주소 XOR 블록 크기
한계 — 내부 단편화
요청: 5KB → 할당: 8KB → 낭비: 3KB
→ 보완하는 것이 슬럽 할당자(SLUB)
버디 시스템 vs 슬럽 할당자
버디 시스템 슬럽 할당자
| 관리 단위 | 페이지 단위 (4KB~8MB) | 객체 단위 (수십~수백 바이트) |
| 역할 | 페이지 프레임 할당 | kmalloc, 커널 구조체 할당 |
| 단편화 방지 | 외부 단편화 | 내부 단편화 |
버디 시스템 → 슬럽 할당자 → kmalloc() → 커널 코드
3. buddyinfo와 메모리 지표 관계
일반 프로세스와 buddyinfo
프로세스 malloc() 소규모
→ glibc 힙에서 처리 → buddyinfo 변화 없음
힙이 부족해 커널에 새 페이지 요청 (brk/mmap)
→ 커널이 버디에서 페이지 꺼냄 → buddyinfo 차감
파일 읽기(페이지 캐시), 커널 내부 구조체 필요
→ 버디에서 직접 할당 → buddyinfo 차감
buddyinfo = 물리 메모리 여유분의 실시간 재고표
buddyinfo vs free vs meminfo 관계
buddyinfo 합산 ≈ MemFree (당장 꺼낼 수 있는 물리 블록)
MemAvailable = MemFree + Cached + SReclaimable(일부)
항목 buddyinfo MemFree MemAvailable
| 버디 free 블록 | ✅ | ✅ | ✅ |
| 페이지 캐시 | ❌ | ❌ | ✅ |
| 슬럽 reclaimable | ❌ | ❌ | ✅ |
cat /proc/meminfo | grep -E "MemFree|MemAvailable|Cached|SReclaimable"
4. 페이지 캐시와 페이지 회수
커널의 메모리 활용 철학
남는 메모리는 페이지 캐시로 활용 → 파일 I/O 성능 향상
프로세스가 메모리 필요 시 → 페이지 캐시를 회수(reclaim) 해서 제공
페이지 회수 메커니즘
메커니즘 동작
| kswapd | 백그라운드에서 미리 페이지 회수 |
| 직접 회수 | 메모리 급부족 시 즉시 회수 |
| OOM killer | 회수해도 부족하면 프로세스 강제 종료 |
5. vm.swappiness / vm.vfs_cache_pressure
vm.swappiness
메모리 부족 시 스왑을 얼마나 적극적으로 사용할 것인가
cat /proc/sys/vm/swappiness # 기본값: 60
값 동작
| 0 | 스왑 최소화 (OOM 위험 증가) |
| 60 | 기본값 |
| 100 | 스왑 적극 사용 |
Raspberry Pi는 SD카드 수명·속도 문제로 낮게 설정 권장 (예: 10)
vm.vfs_cache_pressure
페이지 캐시 / inode / dentry 캐시를 얼마나 적극적으로 회수할 것인가
cat /proc/sys/vm/vfs_cache_pressure # 기본값: 100
값 동작
| 0 | 캐시 절대 안 버림 (메모리 누수 위험) |
| 100 | 기본값, 균형 |
| 200 | 캐시 공격적으로 회수 |
둘의 관계
메모리 부족 시 커널의 선택지
① 익명 메모리(힙/스택) → 스왑으로 ← swappiness 조절
② 페이지 캐시/inode → 회수 ← vfs_cache_pressure 조절
Raspberry Pi 권장 설정
sysctl vm.swappiness vm.vfs_cache_pressure
# 권장값
vm.swappiness = 10
vm.vfs_cache_pressure = 50