본문 바로가기
TIL (Today I Learned)/컴퓨터 시스템(CS)

[pintOS 3] Thierry Snas의 Virtual Memory 개요

by 둥굴프 2023. 1. 3.

https://www.youtube.com/watch?v=HWxMH_SNnNM&ab_channel=ThierrySans 

토론토 대학 pintOS강의
Thierry Sans의 토론토 대학 강의를 참고하여 작성하였습니다.

 

 

지난 프로젝트2에서는 사용자 가상 메모리는 다음과 같이 배치됐다.

USER_STACK +----------------------------------+
           |             user stack           |
           |                 |                |
           |                 |                |
           |                 V                |
           |           grows downward         |
           |                                  |
           |                                  |
           |                                  |
           |                                  |
           |           grows upward           |
           |                 ^                |
           |                 |                |
           |                 |                |
           +----------------------------------+
           | uninitialized data segment (BSS) |
           +----------------------------------+
           |     initialized data segment     |
           +----------------------------------+
           |            code segment          |
 0x400000  +----------------------------------+
           |                                  |
           |                                  |
           |                                  |
           |                                  |
           |                                  |
       0   +----------------------------------+

코드는 0x400000부터 시작하고, 스택은 USER_STACK부터 시작했다.

 

이는 모든 유저 프로그램에 해당했고, 각각의 유저 프로그램은 가상 메모리 덕분에 서로 간섭할 수 없었다.

 

가상 메모리가 일종의 사용자 프로그램에 대한 메모리의 사용자 정의(customizaeed viwe)된 뷰다.

 

유저 프로그램은 시스템에서 단독으로 실행되고 있다는 인상(impression)을 주지만 실제로는 가상 메모리가 물리적 메모리에 매핑된다.

우리가 진행하는 pintOS와 강의에서 설명하는 pintOS의 버전이 달라 표현방법이 다르다.

(강의)phys_base 👉 (kaist-pintOS)USER_STACK

 

pintOS에서 가상 메모리 레이아웃은 page로 나뉘고, User Stack 밑에 있는 User spaceUser stack 위에 있는 Kernel space로 구분된다.

예를 들어, User space의 user stack에 stack을 할당할 때 USER_STACK주소로 user stack에 접근할 수 있다. 하지만, system call을 호출을 수행할 때 커널 공간에서 동일한 사용자 스택에 접근할 수 있다.

이 가상 메모리의 page들은 물리적 메모리에 매핑된다.

물리적 메모리는 frame으로 분할된다. page와 동일한 개념이며 가상 메모리의 모든 page는 물리적 메모리의 frame에 매핑된다.

위 그림에서 User space의 user stack이 Kernel space의 user stack과 동일한 물리적 메모리의 주소에 매핑된 것을 확인할 수 있다.

CPU는 이러한 일련의 과정을 자동적으로 처리하며 이 때, MMU가 사용된다.

우리는 User Page Dir와 Kernel Page Dir 덕분에 유저 가상 주소와 커널 가상 주소를 다룰 수 있었다. 그래서 (커널이든 유저든 상관없이) 실행되고 있는 페이지에 해당하는 디렉토리가 cpu에 load되고, cpu가 번역한다.

 

pintOS에서 user space는 언제 할당될까?

process.c의 static bool load_segment 함수를 확인하면 알 수 있다.

uint8_t *kpage = palloc_get_page (PAL_USER); 코드를 보면 커널 가상 주소 페이지user pool에 할당하는 것을 볼 수 있다.

다음으로 if (!install_page (upage, kpage, writable)) 코드를 보면 사용자 가상 주소 upage에서 커널 가상 주소 kpage로의 매핑을 페이지 테이블에 추가하는 것을 볼 수 있다.

즉, static bool load_segment 함수는 file을 kernel space에 load한 뒤, install_page (upage, kpage, writable) 함수를 통해 user space의 user stack(upage)와 kernel space의 user space(kpage)를 매핑시켜준다.

 

스택영역은 언제 초기화될까?

process.c의 static bool setup_stack 함수를 확인해보자.

kpage = palloc_get_page (PAL_USER | PAL_ZERO); 코드에서 커널 공간에 페이지를 할당한다. 이 때 kpage는 커널에 있는 페이지의 가상주소이다.

 

if_->rsp = USER_STACK 사용자 공간 user stack의 시작 가상 주소이다.

그러면 kernel space의 user stack의 시작 주소는 어디일까?

실제로 유저 가상 주소를 커널 가상 주소로 변환하기 위해서는 page directory를 확인해야 한다.

page directory가 스레드마다(user program마다) 할당되며, 이 디렉토리 주소값을 참조하여 void *pml4_get_page 함수를 통해 대응하는 커널 가상 주소값을 찾을 수 있다.

success = install_page (((uint8_t *) USER_STACK) - PGSIZE, kpage, true); 코드는 user space의 user stack에 1:1로 대응되는 kernel space의 user stack을 매핑하는 기능을 한다.

즉, setup_stack 함수는 물리 메모리에 초기화한 kernel space의 user stack을 user space의 user stack에 1:1 매핑하는 기능을 하는 함수이다.

우리는 이로써 void *pml4_get_page 함수와 vtop 함수를 활용하여 물리 메모리의 주소를 찾아갈 수 있다.

 

메모리 주소를 조작해야 할 때 다음 세 가지의 관점에서 생각해보자.

  1. user virtual address
  2. kernel virtual address
  3. physical address

가상 주소에서 물리 주소로 변환하기 위해서 page directory를 사용해야 한다.

kaist pintOS page directory는 Page Map Level 4를 사용한다.

 

63          48 47            39 38            30 29            21 20         12 11         0
+-------------+----------------+----------------+----------------+-------------+------------+
| Sign Extend |    Page-Map    | Page-Directory | Page-directory |  Page-Table |    Page    |
|             | Level-4 Offset |    Pointer     |     Offset     |   Offset    |   Offset   |
+-------------+----------------+----------------+----------------+-------------+------------+
              |                |                |                |             |            |
              +------- 9 ------+------- 9 ------+------- 9 ------+----- 9 -----+---- 12 ----+
                                          Virtual Address

 

 

어떻게 pml4에서 사용자 가상 주소에 해당하는 물리적 주소를 찾을까?

mmu.c의 pml4_get_page (uint64_t *pml4, const void *uaddr) 함수를 분석해보자.

uint64_t *pte = pml4e_walk (pml4, (uint64_t) uaddr, 0); 을 통해서 page table entry를 찾는다.

pml4e_walk 👉 pdpe_walk 👉 pgdir_walk 👉 return physical address

pgdir_walk 에서 반환하는 return (uint64_t *) ptov (PTE_ADDR (pdp[idx]) + 8 * PTX (va)); 값이 가상 주소에 대한 물리 주소이다.

 

 

위 내용은 내가 notion에 작성한 글을 옮긴 것이다.

notion의 편집과 티스토리의 편집이 달라서 하단에 해당 notion의 주소를 첨부한다.

 

notion : https://puzzle-attempt-70c.notion.site/PintOS-Virtual-Memory-ad147abe873a46039bed0d14614762be