Page Table은 프로세스의 가상 주소 페이지를 물리 주소 프레임으로 매핑해주는 표이다.
시작하기에 앞서,
본 문서는 Page Table의 개념을 설명하기 위해 1-Level Page Table을 기준으로 설명한다.
해당 글에 대한 이해가 끝났으면
x86-64 OS가 사용하는 4-Level Page Table (Multi-Level Paging)에 대한 문서가 있으니
이어서 읽을 것을 권장한다.
Page Table은 다시 말하자면 페이지와 프레임의 대응 관계를 나타내는 표이다.
gef> x/12gx 0xffffa159c2a65000
0xffffa159c2a65000: 0x800000000e386067 0x800000000e387067
0xffffa159c2a65010: 0x800000000e388067 0x800000000e389067
0xffffa159c2a65020: 0x800000000e38a067 0x800000000e38b067
0xffffa159c2a65030: 0x800000000e38c067 0x800000000e38d067
0xffffa159c2a65040: 0x800000000e38e067 0x800000000e38f067
0xffffa159c2a65050: 0x800000000e390067 0x800000000e391067
이해를 돕고자 실제 Page Table이 리눅스 커널에서 구현된 모습을 디버깅하였다.
0xffffa159c2a65000는 가상 주소로, 해당 Page Table의 페이지이다.
페이지이기 때문에 0x1000으로 정렬되어 있음을 확인할 수 있다.
이때 0x800000000e386067 같은 값들은
가상 페이지를 어떤 물리 프레임에 매핑할지와
접근 권한을 함께 담고 있는 Page Table Entry (PTE)이다.
그렇다면 Page Table은 어떻게 동작할까?
Page Table이 본질적으로 하는 일은 딱 하나 뿐이다.
바로 VPN(Virtual Page Number)를 PFN(Physical Frame Number)로 매핑해주는 것이다.
VPN은 가상 주소가 속한 페이지이고,
PFN은 해당 페이지가 올라간 실제 프레임이다.
이때 VPN을 가상 주소와 혼동하면 안된다.
VPN은 Virtual Page “Number”의 약자이다.
즉, 가상 주소 공간에서 이게 몇 번째 페이지인지를 나타내는 번호이다.
이해를 돕기 위해 가상 주소를 직접 VPN으로 변환해보자.
먼저 0x4018ab라는 가상 주소가 있다고 가정해보자.
이때 가상 주소는 [ VPN | Offset ]으로 나뉜다.
이때 VPN은 가상 주소가 속한 페이지의 번호이고
Offset이 해당 페이지에서의 위치인 것이다.
따라서 0x00000000004018ab를 VPN과 Offset으로 변환하게 되면,
VPN = (0x00000000004018ab >> 12) = 0x0000000000401
Offset = 0x8ab
가 되는 것이다.
하위 12비트가 오프셋인 이유는 페이지의 단위가 0x1000이기 때문이다.
즉, VPN은 가상 주소를 페이지 단위로 나눈 페이지의 인덱스인 것이다.
쉽게 말해 0x00000000004018ab안에 저장된 값은
운영체제 입장에서 “0x401 페이지의 0x8ab번째 값”이 되는 것이다.
앞선 과정을 통해 가상 주소 0x4018ab의 VPN이 0x401임을 알 수 있었다.
이제 이 VPN이 어떻게 Page Table을 통해 PFN으로 매핑되는지 살펴보자.
Page Table 또한 Page 단위로 관리되기 때문에,
0x1000 / 0x8 = 0x200 (or 512)
즉, 512개의 PTE를 저장할 수 있다.
이때, 가상 주소 0x4018ab의 VPN은 0x401이므로,
Page Table에서 401번째 인덱스의 값이 해당 가상 주소의 PTE가 된다.
VPN = 0x0000000000401
PTE = page_table[0x401]
이해를 돕기 위해 PTE를 0x800000000e386067라고 가정해보자.
이때 PFN은 (PTE >> 12) & 0x000FFFFFFFFFF가 된다.
PTE의 상위 비트는 PFN이 아닌 NX 여부 등을 표기하는 플래그 값이기 때문이다.
따라서 PTE를 PFN으로 변환하면,
(0x800000000e386067 >> 12) & 0x000FFFFFFFFFF = 0x000000e386이 된다.
PFN을 구하면 이제 최종적으로 가상 주소가 매핑되는 물리 주소를 구할 수 있게 된다.
Physical Address = (PFN << 12) | Offset으로 구하고,
Offset은 가상 주소에서의 Offset과 동일하다.
최종적으로 가상 주소 0x4018ab의 물리 주소를 구하는 과정을 나타내면 아래와 같다:
Virtual Address = 0x4018ab
VPN = (0x00000000004018ab >> 12) = 0x0000000000401
Offset = 0x8ab
PTE = page_table[0x401] = 0x800000000e386067
PFN = (0x800000000e386067 >> 12) & 0x000FFFFFFFFFF = 0x000000e386
Physical Address = (0x000000e386 << 12) | 0x8ab = 0x000000e3868ab
이렇게 Page Table을 통해 가상 주소 0x00000000004018ab는
물리 주소 0x000000e3868ab에 매핑된다는 것을 알 수 있다.
이러한 주소 변환 과정은 Memory Management Unit (MMU)에서 담당한다.