본문 바로가기
카테고리 없음

OS - 가상 메모리 요구 페이징 TLB

by kik328288 2026. 5. 26.

가상 메모리 (요구 페이징, TLB, 주소 변환)

내 노트북의 물리 메모리는 16GB인데, 어떻게 수십 개의 프로그램이 각자 수 GB씩 쓰는 것처럼 동시에 돌아갈까. 답은 가상 메모리(Virtual Memory)다. 운영체제는 각 프로세스에게 "너는 메모리를 통째로 혼자 쓴다"는 거대한 환상을 심어 주고, 실제로는 물리 메모리와 디스크를 영리하게 오가며 그 환상을 떠받친다. 이 추상화 덕분에 프로그래머는 다른 프로세스나 물리 메모리 크기를 신경 쓰지 않고 코드를 짤 수 있다. 본 글은 가상 주소가 물리 주소로 변환되는 과정, 필요할 때만 페이지를 올리는 요구 페이징, 그리고 그 느린 변환을 캐시로 가속하는 TLB를 세심하게 정리한다(출처: 위키백과 — Virtual memory). 제가 운영체제 수업에서 가상 메모리를 배우기 전까지는 포인터가 가리키는 주소가 곧 RAM 칩 위의 물리 위치인 줄 알았는데, "내가 출력한 주소는 사실 가짜였다"는 사실을 알게 된 순간의 충격이 이 주제를 제대로 공부하게 만든 계기였다.

 

가상 주소와 물리 주소 — 변환의 원리

가상 메모리의 핵심은 프로세스가 사용하는 가상 주소(virtual address)와 실제 RAM의 물리 주소(physical address)를 분리하는 것이다. 프로세스가 메모리에 접근할 때마다 CPU 안의 메모리 관리 장치(MMU, Memory Management Unit)가 가상 주소를 물리 주소로 실시간 변환한다. 이 변환의 단위가 페이지(page)이며, 보통 4KB 크기의 고정된 블록이다. 물리 메모리 쪽의 같은 크기 블록은 프레임(frame)이라 부른다.

변환을 위해 운영체제는 프로세스마다 페이지 테이블(page table)을 유지한다. 가상 주소는 두 부분으로 나뉘는데, 앞부분은 페이지 번호, 뒷부분은 페이지 안에서의 오프셋이다. MMU는 페이지 번호로 페이지 테이블을 조회해 대응하는 프레임 번호를 얻고, 거기에 오프셋을 붙여 최종 물리 주소를 완성한다.

[가상 주소 32비트, 페이지 크기 4KB(=2^12)인 경우]

  31                    12 11             0
 ┌───────────────────────┬────────────────┐
 │   페이지 번호 (20비트)  │  오프셋 (12비트) │   ← 가상 주소
 └───────────┬───────────┴────────┬───────┘
             │ 페이지 테이블 조회    │ 그대로 복사
             ▼                     ▼
 ┌───────────────────────┬────────────────┐
 │   프레임 번호           │  오프셋 (12비트) │   ← 물리 주소
 └───────────────────────┴────────────────┘

이 분리가 주는 이점은 세 가지다. 첫째, 각 프로세스가 0번지부터 시작하는 자기만의 연속된 주소 공간을 갖는다는 환상(메모리 격리). 둘째, 프로세스끼리 물리 메모리가 침범되지 않는 보호. 셋째, 물리적으로 흩어진 프레임을 가상에서는 연속처럼 보이게 하는 단편화 완화다. 다만 매 메모리 접근마다 페이지 테이블을 한 번 더 읽어야 하므로, 메모리 접근이 두 배로 느려지는 비용이 따른다. 이 비용을 해결하는 것이 뒤에 나올 TLB다.

요구 페이징과 페이지 폴트

가상 메모리가 물리 메모리보다 큰 주소 공간을 제공할 수 있는 비결이 요구 페이징(Demand Paging)이다. 프로그램의 모든 페이지를 처음부터 메모리에 올리는 것이 아니라, 실제로 그 페이지에 접근하는 순간에만 디스크에서 메모리로 가져온다. 당장 쓰지 않는 페이지는 디스크의 스왑 영역(swap)에 남겨 두므로, 물리 메모리보다 훨씬 큰 프로그램도 실행할 수 있다.

접근하려는 페이지가 아직 메모리에 없을 때 발생하는 것이 페이지 폴트(page fault)다. 이는 오류가 아니라 정상적인 동작 신호로, 다음 순서로 처리된다. MMU가 페이지 테이블에서 "이 페이지는 메모리에 없음"을 확인하면 CPU에 트랩을 걸고, 운영체제가 개입해 디스크에서 해당 페이지를 빈 프레임으로 읽어 온 뒤, 페이지 테이블을 갱신하고 중단됐던 명령을 재실행한다.

여기서 빈 프레임이 없으면 기존 페이지 하나를 디스크로 내보내고(page-out) 자리를 만들어야 하는데, 어떤 페이지를 내보낼지 결정하는 것이 페이지 교체 알고리즘(LRU, FIFO, Clock 등)이다. 이 교체 정책이 잘못되면 스래싱(thrashing)이라는 심각한 문제가 생긴다. 메모리가 부족해 페이지를 올리고 내리는 일에만 CPU 시간을 다 써, 정작 프로그램은 거의 진행하지 못하는 상태다. 제가 메모리가 4GB뿐이던 구형 노트북에서 무거운 IDE와 브라우저를 동시에 켰을 때 디스크는 미친 듯이 돌아가는데 화면은 멈춰 있던 경험이 바로 스래싱이었다.

TLB — 주소 변환을 캐시하다

페이지 테이블 방식의 근본 약점은 매 메모리 접근마다 페이지 테이블을 읽느라 메모리 접근이 한 번 더 필요하다는 점이다. 게다가 현대 시스템은 페이지 테이블이 너무 커서 여러 단계로 나눈 다단계 페이지 테이블을 쓰는데, 그러면 한 번의 변환에 메모리 접근이 두세 번씩 추가된다. 이 치명적 비용을 해결하는 것이 TLB(Translation Lookaside Buffer)다(출처: 위키백과 — Translation lookaside buffer).

TLB는 MMU 안에 있는 작고 빠른 캐시로, 최근에 사용한 "가상 페이지 번호 → 프레임 번호" 변환 결과를 저장한다. CPU가 주소를 변환할 때 먼저 TLB를 확인해, 거기 있으면(TLB hit) 페이지 테이블을 거치지 않고 즉시 프레임 번호를 얻는다. 없으면(TLB miss) 그제야 페이지 테이블을 조회하고, 그 결과를 TLB에 새로 기록한다.

단계 동작 비용
TLB 적중 가상→물리 즉시 변환 약 1 사이클
TLB 부적중 페이지 테이블 조회 후 TLB 갱신 메모리 접근 수 회
페이지 폴트 디스크에서 페이지 로드 수백만 사이클

TLB가 효과적인 이유는 프로그램이 참조 지역성(locality of reference)을 갖기 때문이다. 즉 한 번 접근한 메모리 근처를 잠시 후 다시 접근할 가능성이 높아, 작은 TLB만으로도 적중률이 보통 99% 이상 나온다. 실제 시스템에서 TLB 적중률 1~2%포인트 차이가 전체 성능을 크게 좌우한다.

흔한 오해 하나를 짚으면 "TLB는 데이터를 캐시하는 CPU 캐시(L1·L2)와 같다"는 혼동인데, 둘은 전혀 다르다. L1·L2 캐시는 메모리의 내용을 저장하지만, TLB는 주소 변환 정보만 저장한다. 또 하나, 프로세스가 전환될 때(context switch) TLB의 변환 정보는 이전 프로세스의 것이라 무효가 되므로 비우거나 식별자(ASID)로 구분해야 하는데, 이 TLB 무효화 비용이 잦은 컨텍스트 스위치를 비싸게 만드는 숨은 요인이다. 시험에서는 페이지 폴트 처리 순서와 가상→물리 변환 과정이 단골 출제되고, 실무에서는 스래싱 회피와 TLB 친화적인 메모리 접근 패턴 설계가 성능 튜닝의 핵심이 된다.


메타 디스크립션: 물리 메모리보다 큰 주소 공간의 환상을 만드는 가상 메모리의 동작 원리를, 가상→물리 주소 변환과 페이지 테이블, 필요할 때만 올리는 요구 페이징과 페이지 폴트 처리, 그리고 변환을 가속하는 TLB까지 운영체제 입문자 관점에서 세심하게 정리합니다.


소개 및 문의 · 개인정보처리방침 · 면책조항

© 2026 블로그 이름