MMU는 주소를 번역하면서 권한도 검사한다
프로그램이 보는 virtual address는 RAM chip의 실제 위치가 아니다. CPU의 page-table walker가 여러 단계 descriptor를 따라 physical address를 얻으며, 각 단계에서 read/write/execute, user/kernel, memory type과 access flag를 검사한다.
process가 사용하는 주소. 같은 값이 process마다 다른 memory를 가리킬 수 있다.
VA 구간을 다음 table 또는 final PA block/page로 연결한다.
최근 번역 결과를 저장해 매 접근마다 table을 걷지 않게 한다.
번역이 없거나 권한이 맞지 않을 때 software에 해결 기회를 준다.
TTBR·TCR·MAIR가 번역의 규칙을 정한다
| Register | 핵심 역할 | Linux 관점 |
|---|---|---|
| TTBR0_EL1 | 낮은 VA 영역의 table base와 ASID | 일반적으로 현재 userspace mm의 page table. |
| TTBR1_EL1 | 높은 VA 영역의 table base | kernel mapping. 모든 process가 공유하는 범위가 많다. |
| TCR_EL1 | VA 크기, granule, cacheability, PA 크기 | 4KB/16KB/64KB build와 LPA2 여부를 결정한다. |
| MAIR_EL1 | AttrIndx가 가리키는 memory type | Normal, Device, tagged Normal memory를 구분한다. |
| SCTLR_EL1 | MMU·cache·alignment·보안 검사 활성화 | MMU를 켜는 상위 제어와 process별 일부 상태를 담는다. |
Boot 때 kernel page table을 만든다
초기 assembly가 최소 idmap과 kernel mapping으로 MMU를 켠 뒤 C 초기화가 정식 table을 만든다. Linux v6.18.37의 paging_init()은 memory map과 fixmap을 정리하고, map_mem()은 memblock 범위를 kernel VA에 연결한다.
Boot의 큰 흐름
physical boot address
→ idmap으로 MMU enable
→ swapper_pg_dir의 kernel mapping
→ paging_init()
→ map_mem()
→ memory protections와 fixmap 정리초기 idmap과 최종 kernel mapping은 목적이 다르다. 전자는 MMU를 켜는 순간 현재 PC가 사라지지 않게 하고, 후자는 정상 kernel VA 공간을 제공한다.
사용자 page fault 전체 호출 경로
Translation fault
load/store at EL0
→ do_mem_abort
→ do_page_fault
→ VMA 탐색과 접근 권한 검사
→ handle_mm_fault
→ __handle_mm_fault
→ PUD/PMD/PTE 단계 생성
→ anonymous/file/COW handlerdo_page_fault()는 arm64 syndrome을 Linux VM fault flag로 바꾸고, 공통 MM 코드는 handle_mm_fault()부터 담당한다.
VMA가 없으면 무조건 page를 만드는 것이 아니라 stack 확장 가능성 등을 검사하고 실패하면 SIGSEGV를 보낸다. VMA가 있어도 write fault인데 VM_WRITE가 없으면 permission error다.
처음 읽는 anonymous page는 zero page일 수 있다
malloc()은 흔히 VA 범위만 준비하고 실제 physical page는 첫 접근에 할당한다. read fault라면 공유 read-only zero page를 매핑할 수 있고, write fault라면 새 zeroed folio를 준비해 PTE에 넣는다.
실제 분기점은 do_anonymous_page()다. 메모리 사용량이 malloc() 직후보다 write 후에 증가하는 이유가 이 demand allocation이다.
fork 뒤 첫 write가 Copy-on-write를 만든다
fork() 직후 부모와 자식은 같은 physical page를 read-only로 공유한다. 한쪽이 쓰면 permission fault가 나고 do_wp_page()가 독점 가능 여부를 검사한다. 공유 중이면 새 page를 복사하고 현재 process의 PTE만 바꾼다.
- 기존 PTE와 folio를 안정적으로 잡는다.
- page가 실제로 공유되는지, 그대로 재사용 가능한지 판단한다.
- 필요하면 새 folio를 할당하고 data를 복사한다.
- PTE를 새 PA와 write 권한으로 교체한다.
- 이전 번역을 가진 TLB가 새 권한을 보도록 무효화한다.
PTE는 아무 순서로 덮어쓰면 안 된다
valid mapping을 다른 valid mapping으로 바꿀 때 CPU가 중간 상태를 잘못 관찰하지 않도록 break-before-make 규칙과 barrier·TLBI 순서를 지켜야 한다. 단순히 C 변수 하나를 대입하는 문제가 아니다.
개념적 BBM 순서
old PTE를 invalid로 변경
→ DSB로 table write 가시화
→ 해당 VA/ASID TLBI
→ DSB + ISB
→ new PTE 설치
→ 필요 시 최종 동기화정확한 TLBI opcode와 범위는 mapping 종류와 CPU feature에 따라 달라진다. kernel helper를 우회해 직접 page table을 수정하면 안 된다.
flush_tlb_mm()은 현재 mm의 ASID를 지운다
Linux arm64의 flush_tlb_mm()은 ASID 기반 aside1is 계열 TLBI와 동기화를 사용한다. 한 page만 바꿨는데 전체 system TLB를 지우면 성능이 크게 떨어지므로 kernel은 page·range·ASID·kernel 범위를 구분한다.
관찰 포인트: PTE가 새 값인데 CPU가 옛 권한으로 접근한다면 table 내용뿐 아니라 TLBI 범위, shareability, ASID와 barrier 순서를 검사한다.
Guest에서는 Stage 1 뒤에 Stage 2가 추가된다
guest EL1 page table은 VA를 IPA로 바꾸고, host KVM의 Stage-2 table은 IPA를 PA로 바꾼다. 둘 중 어느 단계에서든 translation·permission fault가 날 수 있다. FAR_EL1과 guest syndrome만 보고 host Stage-2 문제를 확정하면 안 된다.
| 단계 | Table owner | 대표 base/control |
|---|---|---|
| CPU Stage 1 | process kernel 또는 guest OS | TTBR0/1_EL1, TCR_EL1 |
| CPU Stage 2 | host KVM | VTTBR_EL2, VTCR_EL2 |
| SMMU translation | IOMMU driver·VM manager | STE, CD, device page table |
| RME GPC | Root firmware·RMM | GPT; MMU 검사와 별도 |
Linux v6.18.37 소스 지도
| 파일 | 핵심 함수 | 책임 |
|---|---|---|
| arch/arm64/mm/mmu.c | paging_init, map_mem | kernel boot mapping |
| arch/arm64/mm/fault.c | do_page_fault | arm64 syndrome과 VMA 진입 |
| mm/memory.c | handle_mm_fault, do_anonymous_page, do_wp_page | 공통 Linux VM fault 해결 |
| asm/pgtable.h | PTE helper와 descriptor bit | architecture page-table encoding |
| asm/tlbflush.h | flush_tlb_*, __tlbi | 범위별 invalidation과 barrier |
| arch/arm64/mm/context.c | ASID allocation | process address-space 식별 |
Page fault와 TLB를 직접 관찰한다
최소 실험
/usr/bin/time -v ./fault-demo
perf stat -e page-faults,minor-faults,major-faults ./fault-demo
perf trace -e faults:page_fault_user ./fault-demo
cat /proc/$PID/maps
cat /proc/$PID/smapsanonymous 첫 write는 대개 minor fault이고, file data를 storage에서 읽어야 하면 major fault가 될 수 있다. 숫자만 보지 말고 VMA 종류와 workload 접근 패턴을 함께 기록한다.
kernel 개발 환경에서는 CONFIG_DEBUG_VM_PGTABLE, page-table dump와 tracepoint를 사용하되 production 장비의 성능·보안 영향을 고려한다.
근거 자료
- Local sourceLinux v6.18.37 commit
0c503cf3dde2 - ArchitectureA-profile translation regimes
- RegistersTTBR·TCR·MAIR register descriptions
- LinuxLinux arm64 memory layout