32-bit 레지스터를 사용하던 시절엔 사용이 간단했다.
페이지 크기에 맞게 하위 12bit를 걷어내고, 남은 20bit 를 10, 10 으로 쪼개서 각각 Page Directory, Page Table로 운용해서 사용했다.
이를 통해서 총 2^32 bytes, 4GB의 메모리를 사용하는 것처럼 느낄 수 있었다.
https://blog.xenoscr.net/2021/09/06/Exploring-Virtual-Memory-and-Page-Structures.html
각 커널 스레드는 자신만의 페이지 테이블을 가지고 있다. 이는 총 4단계로 이루어지며 순서대로
PML4E 테이블의 entry는 CR3에 저장되어 있으며 pintOS 기준 pml4_activate() 함수를 통해 저장된다.
(Vol. 3A) Figure 4-8
총 4단계에 걸쳐 가상주소의 값이 번역된다.
각각의 offset bit는 9bit이고 2^9 가지수를 표현할 수 있게 된다.
64bit 운영체제를 기준으로 하므로 포인터 주소의 크기는 2^3 bytes
각 단계에서의 페이지 테이블을 생성할 때 2^12 = 4KB의 크기를 가지게 된다. (마침 page의 크기와 동일하다!)
pml4e_walk
주어진 va(pte, palloc_get_page를 통해 얻은 가상 주소)를 pml4e로 활용하여 pte까지 쭉 따라가본다
create flag가 set 되어있으면 없을 때 생성한다.
pml4e[idx] = vtop (new_page) | PTE_U | PTE_W | PTE_P
위의 코드를 보면 user memeory(1) or kernel memory(0)
, Write permission(1) or Read Only(0)
, present(1) or Not present(0)
하위 3비트를 위의 정보를 표시하도록 사용하고 있다.
코드에서 특이한 점이 PDPT에 주소를 저장하는데 물리 주소 로 저장한다.
아래애서 보면 알겠지만 PML4, PDPT, PDT 에 존재하는 주소 값은 물리 주소 이다.
(vtop: virtual to physical 매크로를 이용해서 저장하는 모습)
pdpe_walk
위와 동일하다. 찾아낸 entry를 이용해(pdpe) pdp table을 찾아내고 offset 만큼 이동해 pde를 찾아낸다.
pgdir_walk
위 코드도 동일하게 얻어낸 page directory entry를 이용해서 pd table을 찾아내고 offest만큼 이동한다.
이때 얻어낸 값은 page table entry
이며 이 값은 physical memory와 1대 1 대응이 되는 물리 주소 이다.
pintOS에서는 ptov (physical to virtual) 매크로를 제공하므로 이를 가상주소로 변환하여 반환한다.
(Vol. 3A 4-7)
계층적 페이징 구조. 라고 번역할 수 있다.
우리가 사용하는 주소는 Linear Address라고 표현하며, 64-bit paging에서는 각 구조의 (PML4, PDPT, PDT, PT) 엔트리의 개수가 512개 (2^9) 이다.
page frame: 이러한 엔트리들의 윗부분 (upper potion)은 식별되는 (identifies) physical address의 지역(region)의 주소를 나타낸다.
page offset: 아랫 부분 (lower potion) 은 해당되는 지역에서의 특정한 주소 (specific address)를 식별하기 위해 사용된다.
각 paging structure 에서 또 다른 paging structure로 이동할 때는 reference
라고 표현한다.
page frame으로 가는 주소(마지막 단계)는 map a page
라고 표현한다.
CR3라는 레지스터를 사용한다. 첫 paging structure (pintOS 에서는 pml4 테이블)의 물리 주소 가 CR3 레지스터에 들어있고, 이는 위에서 본 pml4_activate() 함수를 통해서 커널 스레드의 구조체에서 가져온다.