✅ 과제
- Virtual memory (in pintos)
- Supplemental Page Table
- Anonymous Page & Lazy loading
‼️ Virtual memory (in PintOS)
아래 그림을 통해 핀토스의 가상 메모리에 대해 이해해보자.
프로세스들은 독립된 가상 메모리 공간을 가진다. 그리고 가상 메모리 공간은 커널 영역과 유저 영역으로 나뉜다. (어차피 가상 메모리는 페이지 단위로 구성되어있기 때문에 각각을 커널 페이지, 유저 페이지라고 표현한다.)
두 영역의 경계는 KERN_BASE
라는 상수로 vaddr.h
에 선언되어 있으며, 그 값은 0x8004000000
이다. 즉, 가상 메모리의 커널 페이지 영역은 0x8004000000
보다 높은 곳에 위치해있다. 이말인 즉슨 0x8004000000
보다 낮은 곳이 가상 메모리의 유저 페이지 영역이 된다. (+ USER_STACK
상수는 유저 페이지 영역 중 스택 영역이 시작되는 주소이고, 스택은 아래 방향으로 커진다. (stack growth).)
가상 메모리가 왼쪽 그림처럼 생겼다고 하자.. 그럼 가상 메모리는 물리 메모리에 어떻게 매핑이 되어있을까? 핀토스는 가상 메모리의 커널 페이지 영역이 물리 메모리에 1대1로 매핑된다.(그림 상에서 오른쪽) 즉, 가상 메모리 영역의 커널 페이지 영역이 곧 물리 메모리인 것.
KERN_BASE
가 물리 메모리 주소 0에 매핑된다. (핀토스는 단일 프로세스 운영 체제이기 때문에 가상 메모리의 커널 영역이 물리 메모리에 1대 1로 매핑되어도 문제가 되지 않는다. 현재 프로세스가 전체 시스템의 주인이 되어 메모리 및 리소스에 대한 독점적인 접근 권한을 가지기 때문. → 다중 프로세스 운영 체제에서는 가상 메모리 매핑과 관련된 별도의 관리 및 보호 매커니즘이 필요함.)
이제 핀토스에서 프로세스가 생성되었을 때, 가상 메모리와 물리 메모리가 어떻게 구성되어 있는지 알게 되었다. 이제 구성된 메모리를 어떻게 사용하는지에 대해 알아보자.
핀토스에서는 메모리를 할당받기 위한 2가지 할당기를 지원한다. 하나는 페이지 단위로 메모리를 할당받는 palloc
이고, 나머지 하나는 원하는 크기만큼의 메모리를 할당받는 malloc
이다. 일단 palloc
부터 살펴보자. palloc.c
를 살펴보면 알 수 있듯이 palloc_get_page
는 1개의 페이지를 할당받는 함수이고, palloc_get_multiple
은 여러개의 연속된 페이지를 할당받는 함수이다.
두 함수는 공통적으로 flag를 인자로 받는데 이를 이용해서 할당받을 메모리가 속한 영역(PAL_USER
)과 초기화 여부(PAL_ZERO
) 그리고 할당 시 오류가 발생하면 패닉 상태로 전환하는 지(PAL_ASSERT
)의 대한 조건들을 추가로 전달해줄 수 있다. 여기서 중요하다고 생각이 드는건 PAL_USER
다. 해당 flag를 전달해줄 경우엔 가상 메모리의 커널 페이지 영역(핀토스는 가상 메모리 구조에서 커널 페이지 영역이 곧 물리 메모리 영역이다.)에서 유저 풀에 해당하는 영역의 메모리를 할당받을 수 있다. PAL_USER
를 전달해주지 않는다면? 커널 페이지 영역의 커널 풀에 해당하는 영역의 메모리를 할당받을 수 있다.
유저 풀과 커널 풀에 대해 이해하기 위해서 유저 모드와 커널 모드에 대한 선행 지식이 필요하다. 프로세스가 유저모드로 실행되고 있을 때에는 가상 메모리의 유저 페이지 영역의 메모리에만 접근이 가능하고, 커널 모드에서는 커널 풀에 직접 접근하거나 커널 풀에서 메모리를 할당받을 수 있다. (핀토스의 가상 메모리에는 힙영역이 존재하지 않는다. 그럼 유저 모드에서 동적으로 메모리를 할당받지 못한다? → gpt에게 물어본 결과 맞단다.. 즉 핀토스는 커널 모드에서만 메모리를 할당 받을 수 있다.)
다음은 malloc
이다 malloc
은 인자로 전달된 사이즈 만큼의 메모리를 할당해준다. malloc
는 처음엔 desc
배열(디스크럽터들의 배열)을 순회하면서 인자로 들어온 size
만큼의 적합한 블록을 찾아 해당 블록을 할당해준다. 하지만 적합한 크기의 블록이 없는 경우 즉, 디스크립터를 찾지 못한 경우, size
가 너무 큰 것이기 때문에 페이지(PGSIZE
) 단위로 메모리를 할당한다. 이를 위해 여기서 palloc_get_multiple
함수를 호출하여 페이지의 개수만큼 메모리를 할당해준다. (추가적으로 arena? 라는 변수가 존재하는 데 이에 관해서는 더 공부해봐야 할 것 같다.)
결론적으로 핀토스는 힙영역이 존재하지 않기 때문에 정적으로 할당된 메모리 영역만을 사용한다. 즉 핀토스는 프로세스에 필요한 모든 메모리를 미리 할당(프로그램이 로드 될 때)하여 사용하고, 이 작업은 프로세스가 실행될 때 load()
→load_segment()
를 통해서 일어난다.
Project3의 첫번째 ~ 두번째 과제(Memory Menagement ~ Anonymous Page)에서는 페이지 테이블을 보충하는 spt를 만들어서 새로운 방식으로 페이지 폴트를 처리하고 페이지 별로 타입을 두어 실행하는 프로그램의 세그먼트 페이지들을 익명 페이지로 설정해놓고 lazy loading을 시켜주게끔 vm 로직을 새롭게 구현했다. 이 때 spt는 해시 테이블 자료 구조를 사용해서 구현했다.
‼️ Supplemental Page Table
위에서 언급한 대로 프로세스 마다의 spt
를 구성하고, spt
를 이용해서 페이지 폴트를 처리해주어야 한다. spt 테이블을 구현을 위한 자료구조를 선택해야 하므로 난 git book에서 권장하는 hash table을 사용하여 구현했다.(hash table 자료구조에 대한 함수들은 lib/kernel/hash.c
에서 찾을 수 있다.)
해시 테이블의 구조는 위 그림과 같다. bucket
들의 list
가 존재하고 각각의 bucket
에는 page
가 담긴다. 해시 테이블에 페이지를 담아주기 위해서는 해싱 함수가 필요하고 이는 직접 구현해야 한다. 나는 내장되어있던 hash_byte를 이용해서 구현했다.
또한 각 엔트리를 비교하는 hash_less 함수 또한 직접 구현해야 한다. 이 함수는 아직 정확하게 어디서 사용하는지 모르겠다. 일단 코드는 아래처럼 짰다.
단순히 엔트리들의 키 값을 비교해서 순서대로 정렬을 해주는 식으로 구현해주었다. 이제 page fault가 발생했을 때 우선적으로 spt에서 페이지를 찾을 수 있도록, 페이지가 생성되는 즉시 spt에 해당 페이지를 넣어주어야 한다. 새로운 vm 시스템에서 모든 페이지는 vm_alloc_page_with_initializer()
에서 생성되므로 내부에 현재 프로세스(= 스레드)에 새롭게 생성되는 페이지를 넣어주는 코드를 아래와 같이 작성해주면 된다.
‼️ Anonymous Page & Lazy loading
vm 로직을 구현하기 전 핀토스는 어떤 프로그램의 프로세스가 생성될 때, 해당 프로그램에 대한 페이지를 모두 읽어들인 후, 한번에 페이지를 할당해주고 있었다. process.c
의 load_segment()
를 보면
반복적으로 read_bytes
를 읽어서 커널 페이지(kpage
)에 할당해주고, install_page()
를 통해서 해당 페이지에 대한 오프셋을 페이지 테이블에 등록하고, 유저 페이지에 매핑해주고 있다. 결론적으로 load_segment()
가 끝나면 해당 프로그램에 대한 모든 페이지가 물리 메모리에 올라가고, 가상 메모리에도 매핑된다고 볼 수 있다.
하지만 여기서 의문이 하나 생긴다. 쓸지 안쓸지도 모르는 페이지를 미리 물리 메모리에 올려놓을 필요가 있을까? 해당 페이지가 필요할 때 파일을 읽어오면 안되는 걸까? 이러한 의문을 해결하기 위한 방법이 lazy loading 기법이다. 즉 모든 페이지를 spt에 등록해두고 (읽을 파일의 위치와 크기에 대한 정보를 file_segment라는 구조체에 담아 page에 저장해두는 것이 필요하다.) 만약 page fault가 발생한다면? spt를 탐색해서 해당 페이지에 대한 정보를 찾고 정보를 바탕으로 물리 메모리에 올리는 것이다. 이렇게 하면 각 페이지가 필요할 때만 물리 메모리 상에 매핑될 수 있다.
'Development > 운영체제' 카테고리의 다른 글
[운영체제] PintOS - Project 3. Virtual Memory (3) (0) | 2023.06.27 |
---|---|
[운영체제] PintOS - Project 3. Virtual Memory (1) (0) | 2023.06.18 |
[운영체제] PintOS - Project 2. User Program (3) (0) | 2023.06.12 |
[운영체제] PintOS - Project 2. User Program (2) (0) | 2023.06.12 |
[운영체제] PintOS - Project 2. User Program (1) (0) | 2023.06.12 |
댓글