Virtual Address

정글일지 32

Virtual Address

64-비트 가상 주소는 다음과 같은 구조이다

1
2
3
4
5
6
7
8
63          48 47            39 38            30 29            21 20         12 11         0
+-------------+----------------+----------------+----------------+-------------+------------+
| Sign Extend |    Page-Map    | Page-Directory | Page-directory |  Page-Table |  Physical  |
|             | Level-4 Offset |    Pointer     |     Offset     |   Offset    |   Offset   |
+-------------+----------------+----------------+----------------+-------------+------------+
              |                |                |                |             |            |
              +------- 9 ------+------- 9 ------+------- 9 ------+----- 9 -----+---- 12 ----+
                                          Virtual Address

헤더 include/threads/vaddr.h, 그리고 include/threads/mmu.h는 가상 주소들에서 작동하는 이러한 함수들과 매크로들을 정의한다. (offset : 상대주소(relative address), 동일 오브젝트 안에서 오브젝트 처음부터 주어진 요소나 지점까지 변위차를 나타내는 정수형)


1
2
#define PGSHIFT { /* Omit details */ }
#define PGBITS { /* Omit details */ }

비트 index(0) 그리고 비트의 수(12개)는 각자 가상 주소의 offset part이다.


1
#define PGMASK { /* Omit details */ }

page offset bit mask는 1, 나머지 set 는 0


1
#define PGSIZE { /* Omit details */ }

페이지 사이즈는 바이트 (4,096)


1
#define pg_ofs(va) { /* Omit details */ }

가상 주소 va에서 page offset을 추출해 반환


1
#define no(va) { /* Omit details */}

가상 주소 va에서 page number를 추출해 반환


1
#define pg_round_down(va) { /* Omit details */ }

va 가 내부를 가리키는 가상 페이지의 시작지점 반환. 다시 말해, va with the page offset을 0으로 맞춤


1
#define pg_round_up(va) { /* Omit details */ }

가장 가까운 page boundary 까지 rounded up한 va 반환

Pintos에서 가상 메모리는 두 지역으로 나뉘어진다. 유저 가상 메모리와 커널 가상 메모리

그들 사이의 경계는 KERN_BASE:


1
#define KERN_BASE { /* Omit details */ }

커널 가상 메모리의 기본 주소. 기본 설정은 0x8004000000. 유저 가상 메모리는 가상 주소 0에서 KERN_BASE. 커널 가상 메모리 가상 주소 공간의 나머지를 차지한다.


1
2
#define is_user_vaddr(vaddr) { /* Omit details */ }
#define is_kernel_vaddr(vaddr) { /* Omit details */ }

va가 각 유저 가상 주소 혹은 커널 가상 주소면 참, 아니면 거짓 반환


x86-84는 받은 실제 물리적 주소로 메모리에 직접적으로 접근하는 어떤 방법도 제공하지 않는다. 이 능력은 보통 운영체제 커널에 필요하기에, Pintos는 커널 가상 메모리를 실제 물리적 메모리에 일대일 대응으로 mapping 하는 방식으로 작동한다. 즉, KERN_BASE 라는 가상주소는 실제 주소 0에 접근하고, 가상 주소 KERN_BASE + 0x1234는 실제주소 0x1234에 접근하는 방식이 계속해서 기계의 실제 물리적 메모리 크기의 따라 계속된다. 따라서, KERN_BASE를 실제 주소에 더하여서 그 주소에 접근하는 커널 가상 주소를 얻을 수 있다. 정반대로, KERN_BASE를 커널 가상 주소에서 뽑아내어서 그에 상응하는 실제 주소를 얻을수 도 있다.

헤더 include/threads/vaddr.h는 이러한 ‘번역’을 해주는 함수 짝을 제공한다.


1
#define ptov(paddr) { /* Omit details */ }

실제 물리적 주소 pa에 상응하는 커널 가상 주소를 반환한다. 이 pa는 0과 실제 메모리의 바이트 수 의 사이 값이여야 한다.


1
#define vtop(vaddr) { /* Omit details */ }

va에 상응하는 실제 물리적 주소를 반환한다. va는 반드시 커널 가상 주소여야 한다.

헤더 include/threads/mmu.h는 페이지 테이블에 대한 operation을 다음과 같이 제공한다:


1
2
#define is_user_pte(pte) { /* Omit details */ }
#define is_kern_pte(pte) { /* Omit details */ }

각각, PTE(Page Table Entry)가 유저나 커널이 소유하는 것인지 묻는다.


1
#define is_writable(pte) { /* Omit details */ }

PTE(Page Table Entry)가 가리키는 가상 주소가 writable한지 묻는다.


1
2
typedef bool pte_for_each_func (uint64_t *pte, void *va, void *aux);
bool pm14_for_each (uint64_t *pm14, pte_for_each_func *func, void *aux);

PML4 하위에 있는 각각 타당한 entry에 auxiliary value로 FUNC를 적용한다. VA는 entry의 가상 주소를 나타낸다. pte_for_each_func가 false를 반환하면, 반복을 멈추고 false를 반환한다.

아래는 pm14_for_each에 들어갈 func의 예시를 보여준다:

1
2
3
4
5
6
7
8
static bool
stat_page (uint64_t *pte, void *va, void *aux) {
    if (is_user_vaddr (va))
        printf ("user page: %llx\n", va);
    if (is_writable (va))
        printf ("writable page: %llx\n", va);
    return true;
}