페이징: 개요
운영체제는 공간 관리 문제를 해결할 때 크게 두 가지 방법으로 세그멘테이션이나 페이징을 사용한다.
1. 간단한 예제 및 개요
예제 프로세스 & 물리 메모리
- 예제 프로세스

- 총 크기: 64바이트
- 페이지 수: 4개 (0~3)
- 페이지 크기: 16바이트
- 예제 물리 메모리

- 총 크기: 128바이트
- 수용 가능 페이지 수: 총 8 페이지
- 운영체제를 위해 예약된 물리 메모리도 존재
세그멘테이션과 같은 이전 방식에 비해 유연하다.
- 페이징을 사용하면 프로세스의 주소 공간 사용 방식과는 상관없이 효율적으로 주소 공간 개념을 지원할 수 있다.
- 힙과 스택이 어느 방향으로 커지는지 상관 x
- 힙과 스택이 어떻게 사용되는지 알 필요 없음
또 다른 장점은 빈 공간 관리의 단순함이다.
- 위의 예에서 64바이트 주소공간을 물리 메모리에 배치하길 원한다면, 운영체제는 비어있는 네 개의 페이지만 찾으면 됨 (빈 공간 리스트)
- 위의 예에서 운영체제는 가상 페이지 0,1,2,3을 각각 물리 페이지 프레임 3,7,5,2에 배치
페이지 테이블
- 프로세스마다 주소 공간의 각 가상 페이지에 대한 물리 메모리 위치를 기록하는 테이블
(가상페이지 대 물리프레임 매핑)
- 역할: 주소 공간의 가상 페이지 주소 변환 정보를 저장
- 위의 예에서는
VP0->PF3
, VP1->PF7
, VP2->PF5
, VP3->PF2
항목을 가짐
- 페이지 테이블은 프로세스마다 존재 (역 페이지 테이블이라는 예외적인 기법도 있긴함)
주소 변환
가상주소
-> 물리주소
변환을 위해 가상 페이지 번호(Virtual page number, VPN)이 필요
- 위의 예에서는 가상 주소 공간의 크기가 64바이트이므로, 가상 주소는 6비트가 필요

- 총 크기는 64바이트, 한 페이지의 크기는 16바이트이므로 총 4페이지를 선택할 수 있어야 한다.
따라서 상위 2비트가 가상 페이지 번호를 가지게 된다.
- 나머지 비트는 한 페이지 내에서 우리가 원하는 바이트의 위치를 나타내고, 이를 오프셋이라고 부른다.
- ex) 가상 주소 21
- 21 -> 010101(2) -> 01(VPN) 0101(오프셋)
- 따라서 가상주소 21은 가상페이지 1번의 5번째 바이트이다.
- 아까 페이지 테이블에
VP1->PF7
이 저장되어 있으므로, 가상주소 1을 넣으면 물리 프레임 번호(Physical frame number, PFN)은 7이 나온다.
(이때 오프셋은 상대적인 위치이므로 변하지 않는다.)
2. 페이지 테이블은 어디에 저장되는가
페이지 테이블은 세그먼트 테이블이나 베이스-바운드 쌍에 비해 매우 커질 수 있다.
- ex) 페이지의 크기가 4KB인 32비트 주소 공간
- 페이지 크기 4KB = 4096 = 212
- 따라서 32비트 주소공간에서 오프셋은 12비트이고, 그에 따라 VPN은 20비트
- 20비트 VPN의 의미는 "운영체제가 각 프로세스를 위해 관리해야 하는 변환의 수가 220개" (약 백만)
- 만약 물리 주소로의 변환 정보와 다른 필요한 정보를 저장하기 위해 페이지 테이블 항목(Page table entry, PTE)마다 4바이트가 필요로 한다면 4MB의 메모리가 필요, 프로세스가 100개 실행중이면 주소 변환을 위해 400MB 필요...
- 페이지 테이블은 매우 크기 때문에, 현재 실행 중인 프로세스의 페이지 테이블을 저장할 수 있는 회로를 MMU 안에 유지하지 않을 것
- 대신 각 프로세스의 페이지 테이블을 메모리에 저장한다.
(당분간 페이지 테이블은 물리 메모리에 상주한다고 가정)

3. 페이지 테이블에는 실제 무엇이 있는가
선형 페이지 테이블(linear page table)
- 가상 페이지 번호(VPN)를 물리 주소(PFN)로 매핑하는데 사용되는 단순한 배열
- 가상 페이지 번호(VPN)로 페이지 테이블에 접근
- 페이지 테이블 항목(PTE) 검색
- 물리 페이지 번호(PFN)
- 페이지 테이블 항목(PTE)
- Valid bit: 특정 변환의 유효 여부를 나타내는 비트, 할당되지 않은 주소 공간 표현
- Protection bit: 페이지가 읽을 수 있는지, 쓸 수 있는지, 실행될 수 있는지를 나타내는 비트
- Present bit: 페이지가 물리메모리 혹은 디스크에 있는지 (=스왑 아웃 되었는지) 여부를 나타내는 비트
- Dirty bit: 메모리에 반입된 후 페이지의 변경 여부를 나타내는 비트
- Reference bit(accessed bit): 페이지가 접근되었는지를 추적 (페이지 교체에 매우 중요)

- x86 아키텍쳐의 PTE
- Present bit(P), Read/Write bit(R/W), Accessed bit(A), Dirty bit(D) 등 다양한 비트, 그리고 PFN 자체로 구성됨
4. 페이징: 너무 느림
페이지 테이블의 크기가 메모리 상에서 매우 크게 증가할 수 있고, 그로 인해 처리 속도가 저하될 수 있다.
- 예시) 주소 21에 대해 참조하는 명령어 실행 (
movl 21, %eax
)
- 하드웨어가 주소 변환 담당한다고 가정
- 과정: 가상주소(21) -> 페이지 테이블에서 적절한 PTE 가져오기 -> 변환 수행 후 물리 메모리에서 데이터 탑재
- 이렇게 하기 위해서, 하드웨어는 현재 실행 중인 프로세스의 페이지 테이블의 위치를 알아야함
그러므로 페이지 테이블 베이스 레지스터가 페이지 테이블의 시작 (물리)주소를 저장한다고 가정
- 원하는 PTE 위치를 찾기 위해 하드웨어는 다음과 같은 연산 수행
VPN = (VirtualAddress & VPN_MASK) >> SHIFT
PTEAddr = PageTableBaseRegister + (VPN * sizeof(PTE))
VPN_MASK
는 110000(2)
으로 VPN 비트만 골라낸다
- 이후
SHIFT
는 4로 설정되어(오프셋 비트 수) VPN 비트를 오른쪽으로 이동
- 가상주소 21(010101)을 마스킹하면 010000, 이후 쉬프트 연산을 하면 01 (가상페이지 1로 변환)
- 이 값을 페이지 테이블 베이스 레지스터가 가리키는 PTE 배열에 대한 인덱스로 사용
PTE = AccessMemory(PTEAddr)
offset = VirtualAddress & OFFSET_MASK
PhysAddr = (PTE.PFN << PFN_SHIFT) | offset
- 하드웨어는 메모리에서 PTE를 반입
OFFSET_MASK
는 001111(2)
로 오프셋 비트만 골라낸다
PFN_SHIFT
는 4로 설정되고(오프셋 비트 수), 이를 오프셋과 연결하여 물리 주소를 만든다
Register = AccessMemory(PhysAddr)
- 마지막으로 하드웨어는 메모리에서 원하는 데이터를 가져와서 레지스터에 넣음
- 메모리 참조 전체 과정
VPN = (VirtualAddress & VPN_MASK) >> SHIFT
PTEAddr = PageTableBaseRegister + (VPN * sizeof(PTE))
PTE = AccessMemory(PTEAddr)
if (PTE. Valid == False)
RaiseException(SEGMENTATION_FAULT)
else if (CanAccess(PTE. ProtectBits) == False)
RaiseException(PROTECTION_FAULT)
else {
offset = VirtualAddress & OFFSET_MASK
PhysAddr = (PTE.PFN << PFN_SHIFT) | offset
Register = AccessMemory(PhysAddr)
}
- PTE 반입 시 메모리 참조, 그리고 레지스터에 데이터 넣을 때 메모리 참조, 총 두 번의 메모리 참조가 일어남
- 메모리 참조는 비용이 비싸고, 이 경우에 프로세스는 2배 이상 느려진다.
페이징 기법을 쓸 때 해결해야 할 문제점 2가지
1. 소프트웨어 및 하드웨어 설계 잘못하면 페이지 테이블로 인해 시스템이 매우 느려질 수 있음
2. 너무 많은 메모리를 차지함
5. 메모리 트레이스
페이징을 사용했을 때 발생하는 모든 메모리 접근을 살펴보자
int arrray[1000];
...
for(i=0; i<1000; i++)
array[i] = 0;
- 위의 코드가 어떤 메모리 접근을 생성하는지 이해하기 위해 어셈블리어로 바꿔줌
0x1024 movl $0x0, (%edi,%eax,4)
0x1028 incl %eax
0x102c cmpl $0x03e8,%eax
0x1030 jne 0x1024
- 이 명령어 시퀀스가 가상 및 물리 수준 모드에서 어떤 메모리 접근을 생성하는지 이해하기 위해서 몇가지 가정이 필요
- 이 예에서 크기가 64KB인 가상 주소 공간을 가정, 페이지의 크기는 1KB로 가정
- 테이블은 선형 페이지 테이블이고, 물리주소 1KB(1024)에 위치
- 코드가 상주하는 가상 페이지의 가상주소는 1KB(1024)이며, 이는 가상 주소 공간의 두번째 페이지(VPN=1)이다. 또한 이 가상 페이지는 물리 프레임 4에 매핑됨 (
VPN1->PFN4
).
- 크기가 4000바이트인 (1000개의 정수) 배열이 가상주소 40000에서 44000까지 존재한다고 가정. 이 범위의 가상 페이지는 VPN=39~42이며(페이지 크기 1KB이므로 4페이지 필요) 가상-대-물리 주소 매핑은
(VPN39->PFN7)
VPN40->PFN8
VPN41->PFN9
VPN42->PFN10
이다.
- 프로그램이 실행되면, 각 명령어 반입 시에 메모리가 두 번 참조한다
- 명령어의 위치를 파악하기 위해 페이지 테이블 접근 한 번 참조
- 명령어 자체에 한 번 참조
- 추가적으로
mov
명령어도 메모리 참조를 함
- 배열의 가상 주소를 올바른 물리 메모리 주소로 변환하기 위한 페이지 테이블 접근 한 번 참조
- 다음 배열 자체를 접근하기 위해 한 번 참조
- 처음 다섯 번의 루프 반복에 대한 전체 과정

- 가장 위쪽 그래프가 페이지 테이블 메모리 접근 (페이지 테이블은 물리 메모리만 존재)
- 가운데 그래프가 배열에 대한 접근 (왼쪽이 가상주소, 오른쪽이 물리주소)
- 가장 아래쪽 그래프가 명령어 메모리 참조 (왼쪽이 가상주소, 오른쪽이 물리주소)
- 루프당 10번의 메모리 접근
- 4번의 명령어 반입 (코드부)
- 한 번의 메모리 갱신 (배열)
- 4번의 반입(코드부에 대응)과 한 번의 "명시적인 갱신을 위한 주소 변환(배열 갱신)"을 위한 총 다섯번의 페이지 테이블 접근
6. 요약
- 페이징은 메모리를 고정 크기의 단위로 나눈다
- 가상 주소 공간이 드문드문 사용되는 것을 허용
- 근데 페이징 제대로 설계 못하면
- 매우 느림 (페이지 테이블 접근을 위한 많은 추가적 접근)
- 메모리 낭비 심함 (페이지 테이블로 가득 찰 수도)
- 제대로 동작하는 페이징 시스템을 고안해야 한다