Introduction
* 회색으로 표시해놓은 부분은 개념이 헷갈리거나 모르는 부분
* Special Thanks to class B translators ..~
Project3: Virtual Memory
당신의 OS는 여러 개의 쓰레드를 알맞게 동기화해서 적절히 핸들링할 수 있고, 여러개의 유저 프로그램을 한번에 로드할 수 있다. 그러나, 돌릴 수 있는 프로그램의 개수와 사이즈는 머신의 메인 메모리 크기에 맞춰 제한되어있다.
이번 과제를 통해 무한의 메모리의 환상(을 만들어서 그 제한을 제거해보자. 🕗
💡 프로젝트 3을 진행함에 있어 편의를 위해 단계별 가이드를 제공한다.
1. Background 배경
1-1) Source Files 소스파일
- 이번 프로젝트에서는
vm
디렉토리에서 작업하게 될 것이다. Makefile
은-DVM
을 setting하도록 되어있다.
⇒ Makefile에서 VM에 대해 define 해준다는 말이다. 코드 중간에 ifdef VM으로 감싼 부분이 활성화된다.- 엄청난 양의 템플릿 코드를 제공합니다.
- 반드시 주어진 템플릿을 따르세요. 이 말은, 주어진 템플릿에 기반하지 않고 코드를 제출한다면 당신은 0점을 받는다...!
- 또한, “DO NOT CHANGE”라고 적혀있는 부분의 코드는 절대 수정하지 마라. 아래에 각 템플릿 파일에서 당신이 수정하게 될 부분에 대한 자세한 설명을 하겠다.
- 고쳐야 하는 템플릿 파일들의 상세 내용을 설명하면 아래와 같다.
include/vm/vm.h
,vm/vm.c
- 가상 메모리를 위한 일반적인 인터페이스를 제공한다.
- 헤더 파일(vm.h)에서는 당신의 가상 메모리 시스템이 지원해야 하는 각기 다른 vm_type(가상 메모리 타입)들의 정의와 설명을 볼 수 있다.
- vm_type(가상 메모리 타입)
- VM_ANON(파일과 relate 되지 않은 페이지, aka anonymous page)
- VM_PAGE_CACHE(페이지 캐시를 가진 페이지)
- VM_FILE(파일과 relate 된 페이지)
- VM_UNINIT(초기화 되지 않은 페이지)
- vm_type(가상 메모리 타입)
- VM_PAGE_CACHE는 프로젝트 4를 위한 것이니 무시!!
- You will also implement your supplementary page table here.
⇒ supplementary page table을 여기에 구현하게 될 것이다.
include/vm/uninit.h
,vm/uninit.c
- 초기화되지 않은 페이지(uninitialized page)들을 위한 기능을 제공합니다. (vm_type =
VM_UNINIT
) - 현재 설계에서는, 모든 페이지가 초기화되지 않은 페이지로 설정되고, 이후 anonymous page나 file-backed page로 변환된다.
- 초기화되지 않은 페이지(uninitialized page)들을 위한 기능을 제공합니다. (vm_type =
include/vm/anon.h
,vm/anon.c
- anonymous page를 위한 기능을 제공한다. (vm_type =
VM_ANON
).
- anonymous page를 위한 기능을 제공한다. (vm_type =
include/vm/file.h
,vm/file.c
- file-backed page를 위한 기능을 제공한다. (vm_type =
VM_FILE
).
- file-backed page를 위한 기능을 제공한다. (vm_type =
include/vm/inspect.h
,vm/inspect.c
- 채점을 위한 메모리 검사 기능이 포함되어 있다.
- 이 파일은 손대지 말자.
include/devices/block.h
,devices/block.c
- 블록 디바이스로의 섹터 기반 읽기/쓰기를 제공한다. 이 인터페이스는 블록 디바이스로 스왑 파티션에 접근할 때 사용하게 될 것이다.
- ❗️😲
include/devices/block.h
,devices/block.c
가 아닌include/devices/disk.h
,devices/disk.c
로 봐야한다.(메뉴얼이 업데이트 안되었대용~)
2. Memory Terminology 메모리 용어
- Pages, Frames, Page Tables, Swap Slots을 소개할 것이다. 이 파트는 꼭 꼼꼼하게 봐야한다.
- 메모리와 저장소에 관한 몇 가지 용어들을 소개하면서 시작하겠다. 몇몇 용어들은 프로젝트 2에서 봤던 용어이기도 하겠지만, 대부분 새로 접하는 내용일 것이다. (자 .. 다시 Project 2의 Gitbook을 읽어보자 ~~~)
2-1) Project 2 Introduction - Virtual Memory Layout 💡✨
- 핀토스의 가상 메모리는 2개의 영역으로 나눌 수 있다.
- 1) 유저 가상 메모리 : 0 ~ KERN_BASE
- 2) 커널 가상 메모리 : KERN_BASE ~ ????
- 유저 가상 메모리는 가상 주소 0부터 KERN_BASE까지의 범위를 가진다. KERN_BASE는 include/threads/vaddr.h에 정의되어 있고 기본적으로
0x8004000000
입니다. 커널 가상 메모리는 가상 주소 공간의 나머지를 차지한다.
- 하나의 프로세스는 하나의 유저 가상 메모리를 가진다. (그렇쳐~)
- 프로세스 문맥 교환이 일어날 때, 커널은 프로세스의 ‘page directory base register(see
pml4_activate()
inthread/mmu.c
)를 바꿈으로써 유저 가상 주소 공간 또한 바꿔준다. - 스레드 구조체는 하나의 프로세스의 페이지 테이블을 가리키는 포인터를 가지고 있다.
⇒ *pml4
- 커널 가상 메모리는 전역적입니다. (그렇쳐~)
- 커널 가상 메모리는 어떠한 유저 프로세스 또는 커널 스레드가 CPU 제어권을 획득해 running인지에 관계없이 항상 같은 방식으로 매핑된다. ⇒ 모든 유저 프로세스 VM의 Kernel VM은 항상 동일하다.
- 핀토스에서
KERN_BASE
에서 시작하는 커널 가상 메모리는 물리 메모리와 일대일 매핑이 됩니다. 다시 말해, 가상 주소인KERN_BASE
는 물리주소0
에 매핑이 되고, 가상 주소KERN_BASE + 0x1234
물리주소0x1234
에 매핑된다.
- 유저 프로그램은 자신의 유저 가상 메모리에만 접근할 수 있다.
- 유저 프로그램이 커널 가상 메모리에 접근하려는 시도는 page fault를 야기하고 프로세스는 종료된다.
- page fault는
userprog/exception.c
에 있는page_fault()
라는 함수에 의해 이루어진다. - 한편, 커널 스레드들은 커널 가상 메모리에 접근 가능하고 만일 유저 프로세스가 running 상태라면 이 유저 프로세스의 유저 가상 메모리에도 접근할 수 있다.
- 하지만, 심지어 커널에서 매핑되지 않은 유저 가상 주소로 메모리에 접근하려는 시도조차도 page fault를 야기한다.
- page fault가 일어나는 경우
- 유저 프로그램이 커널 가상 메모리에 접근하려고 할 때
- 커널에서 매핑되지 않은 유저 가상 주소로 접근하려고 할 때
2-2) Project 2 Introduction - Typical Memory Layout 💡✨
- 각각의 프로세스는 자유롭게 자신의 가상 메모리를 배치할 수 있다. 유저 가상 메모리는 아래와 같은 레이아웃을 가진다.
- 이 프로젝트에서, 유저 스택의 크기는 고정되어 있지만, 프로젝트 3에서는 유저 스택의 크기는 가변적이게 된다.
⇒ 유저 스택의 크기를 가변적으로 만들면 됨. - 핀토스에서 코드 세그먼트는 가상 메모리 0x400000에서 시작하고, 대략 주소 공간의 바닥에서 128MB만큼 떨어져있다.
자.. 다시 Project 3의 Pages, Frames, Page Tables, Swap Slots을 소개할 것이다. 이 파트는 꼭 꼼꼼하게 봐야한다.
2-3) Pages
- “페이지”는 가상 페이지(virtual pages)라고도 불린다.
- “페이지”는 4,096 바이트(4KiB = 페이지 크기)의 길이를 가지는 가상 메모리의 연속된 영역입니다.
- 말이 어려워서 그렇지 page는 가상 메모리를 일정한 크기(4KiB)로 나눈 블록이라고 생각하면 된다.
- “페이지”는 항상 page-aligned되어있어야 한다.
- 즉, 각 페이지는 페이지 크기(4KiB)로 균등하게 나누어지는 가상 주소에서 시작해야 한다.
- 시작 위치는 항상 page size로 나누어 떨어져야 한다.
- 64비트 가상 주소에서 하위 12비트는 page offset이다. (2^12 = 4096)
- 상위 비트들은 페이지 테이블의 인덱스로 사용된다. (⇒ Sign Extend 부분을 말하는 건가?)
- 64비트 시스템에서 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
- 각 프로세스는 가상 주소
KERN_BASE
(0x8004000000)이하의 독립적인 유저 (가상) 페이지 집합을 가지고 있다.
⇒ 유저 가상 페이지 집합 = 유저 VM - 커널 (가상) 페이지들은 전역적이고, 어떤 스레드 또는 프로세스가 실행 중이어도 동일한 위치가 유지된다.
- 커널은 유저와 커널 페이지를 모두 접근할 수 있지만, 유저 프로세스는 스스로의 유저 페이지들에만 접근이 가능하다.
- 참고자료 : Intel X86 Page Table - 가상주소와 PML4 구조 이해에 도움이 됩니다. (출처: 권영진 교수님 교육자료)
2-4) Frames
- 물리 프레임(physical frame) 또는 페이지 프레임(Page frame)이라고도 불리는 프레임(frame)은 물리 메모리상의 연속적인 공간을 의미한다.
- 말이 어려워서 그렇지 frame은 물리 메모리를 일정한 크기(4KiB)로 나눈 블록이라고 생각하면 된다.
- 페이지와 동일하게, 프레임은 페이지 사이즈여야 하고 페이지 크기에 정렬되어 있어야 한다.
(페이지 사이즈 = 프레임 사이즈 = 4KiB) - pages와 동일하게 frame 또한 page-size를 가지고 page-aligned되어야 한다.
- 64비트 물리 주소는 아래와 같이 frame number와 frame offset(또는 그냥 오프셋)으로 나누어진다.
12 11 0
+-----------------------+-----------+
| Frame Number | Offset |
+-----------------------+-----------+
Physical Address
- x86-64는 물리 메모리에 있는 메모리에 직접적으로 접근하는 방법을 제공하지 않는다.
- Pintos는 커널 가상 메모리를 물리 메모리에 직접 매핑하여 이 작업을 수행한다.
- 커널 가상 메모리의 첫 번째 페이지는 물리 메모리의 첫번째 프레임에 매핑되고 두 번째 페이지는 물리 메모리의 두 번째 프레임에 매핑된다.
- 커널 가상 메모리를 통해 프레임에 접근 할 수 있다.
- Pintos는 physical address와 Kernel virtual address사이의 변환을 수행하는 함수를 제공한다.
ptov
: physical to vitual,vtop
: virtual to physical
#define ptov(paddr) : Returns the kernel virtual address corresponding to physical address pa
#define vtop(vaddr) : Returns the physical address corresponding to va
2-5) Page Tables
- CPU가 가상 주소를 물리 주소로 변환할 때 사용하는 자료 구조 (page → frame)
- 페이지 테이블 형식은 x86-64 아키텍처에 의해 결정된다.
- 페이지 테이블 관련 코드는
threads/mmu.c
에 있다.
- 페이지 테이블 관련 코드는
- 아래의 그림은 페이지와 프레임의 관계를 나타낸다.
- 왼쪽의 가상 주소는 페이지 번호와 오프셋으로 구성되어있다.
- 페이지 테이블은 페이지 번호를 프레임 번호로 변환하며, 프레임 넘버는 오른쪽에 보이는 것처럼 물리 주소를 획득하기 위한 미수정된 오프셋과 결합되어 있다. (미수정된 오프셋??? ⇒ 오프셋은 always 똑같다 !!)
+----------+
.--------------->|Page Table|-----------.
/ +----------+ |
| 12 11 0 V 12 11 0
+---------+----+ +---------+----+
| Page Nr | Ofs| |Frame Nr | Ofs|
+---------+----+ +---------+----+
Virt Addr | Phys Addr ^
\_______________________________________/
2-6) Swap Slots
- 스왑 슬롯(swap slot)은 스왑 파티션(swap partition) 내의 디스크 공간에 있는 페이지 크기의 영역입니다.
⇒ swap은 disk의 한 영역이다. - 하드웨어적 제한들로 인해 배치가 강제되는 것(정렬)이 프레임에서보단 슬롯에서 더 유연한 편이지만, 정렬한다고 해서 별다른 부정적인 영향이 생기는 건 아니기 때문에 스왑 슬롯은 페이지 크기에 정렬하는 것이 좋다.
3. Resource Management Overview 자원 관리 개요
📖 다음의 자료 구조에 대해 설계 및 구현을 해야한다.
3-1) page table 페이지 테이블: 가상 주소를 물리 주소로 바꿔주는 역할을 하는 자료구조
3-2) Supplemental page table 보조 페이지 테이블: 페이지 테이블에서 부족한 정보를 채워주기 위해 프로세스마다 존재하는 table/페이지 테이블을 보조해서, 페이지 폴트 핸들링이 가능하도록 해준다.
3-3) Frame table 프레임 테이블: 현재 사용 중인 frame들을 관리하기 위한 table / 물리 프레임의 방출 정책(eviction policy)를 구현하는 것을 가능케한다.
3-4) Swap table 스왑 테이블: swap 영역 관리를 위한 table / swap slots의 사용을 추적한다.
3-5) Choices of implementation (Performance perspective) 성능 관점에서 구현 선택
- 배열, 리스트, 비트맵, 해시 테이블 중 하나를 선택해서 구현할 수 있다.
- 배열은 가장 간단한 방법이지만 sparse한 배열은 메모리를 낭비한다.
- 리스트 또한 간단하지만 긴 리스트를 순회하는 것은 시간이 낭비된다.
- 배열과 리스트 모두 크기를 다시 지정할 수 있지만 리스트가 중간에 삽입, 살제를 더 효율적으로 지원한다.
- 핀토스는 비트맵 자료구조 또한 제공한다. (
lib/kernel/bitmap.c
,include/lib/kernel/bitmap.h
) - Pintos는 또한 해시 테이블 자료구조를 포함하고 있다. Pintos 해시 테이블은 다양한 테이블 크기에서의 삽입과 삭제를 효율적으로 지원한다. ⇒ 아마 해시 테이블 쓸 듯??
- 더 복잡한 자료구조는 되도록 사용하지 말자.
4. Managing the Supplemental Page Table
- 💡SPT(Supplemental Page Table)란?
- pml4(페이지 테이블)에서 부족한 정보를 채워주기 위한 프로세스마다 존재하는 테이블
- 💡왜 SPT를 사용해야하는지?
- Page fault시 SPT에서 오류가 발생한 Page를 조회하여 데이터가 있어야할 위치를 확인
- Process가 종료될 때 Kernel이 SPT를 참조하여 어떤 resource를 free할 것인지를 결정
💡 권영진 교수님 강의 자료에서의 SPT의 정의
SPT ⇒ 아래의 보조 데이터들을 담고있는, 프로세스마다 존재하는 자료구조
- 각각의 페이지에 대해서 데이터가 존재하는 곳 (frame, disk, swap 중 어디 존재하는지)
- 이에 상응하는 커널 가상 주소를 가리키는 포인터 정보
- page와 관련된 추가 정보 (active인지 inactive인지 등등)
4-1) Organization of Supplemental Page Table
- spt는 하고싶은대로 구현하면 된다.
- 두 가지 방식의 구조를 가질 수 있는데, 세그먼트 기반, 페이지 기반 방식이 있다.
- 여기서 말하는 세그멘트는 연속적인 페이지들의 그룹을 말한다.
- executable을 포함하는 메모리 영역
- memory-mapped file을 포함하는 메모리 영역
- 여기서 말하는 세그멘트는 연속적인 페이지들의 그룹을 말한다.
- advanced에 도전하고 싶은 경우 페이지 테이블을 수정하여 spt를 포함하도록 구현할 수 있다.
- 이때는 threads/mmu.c를 수정하면 된다.
4-2) Handling page fault 페이지 폴트 다루기
- SPT(Supplemental Page Table)의 핵심 유저는 페이지 폴트 핸들러다.
- 프로젝트 2에서는 페이지 폴트는 항상 커널이나 유저 프로그램의 버그를 의미했다.
- 프로젝트 3부터는 페이지 폴트는 그 페이지가 파일이나 스왑 슬롯에서 가져와야 함을 나타낸다.
- 더 복잡한 페이지 폴트 핸들러를 구현하여 이 경우를 처리할 수 있도록 하자.
userprog/exception.c
의page_fault()
는vm/vm.c
에 있는 당신이 구현한vm_try_handle_fault()
를 호출한다.- 구현해야하는 기능들은 대략 아래와 같다.
- SPT(Supplemental Page Table)에서 페이지 폴트가 발생한 페이지를 찾는다. 메모리 참조가 유효하면 SPTE(SPT Entry)를 사용해서 페이지에 들어갈 데이터를 찾는다.
- 데이터는 파일 시스템에 있거나, 스왑 슬롯에 있거나, 단순히 0으로만 이루어진 페이지(all-zero페이지)일 수 있다.
- 만약 sharing(i.e., Copy-on-Write)을 구현한다면 페이지의 데이터는 페이지 프레임에 있지만 페이지 테이블에는 없을 수도 있다.
- 만약 SPT이 다음과 같은 정보를 보여주고 있다면 그 접근은 유효하지 않은 접근이다.
- 유저 프로세스가 접근하려는 주소에서 어떤 데이터도 얻을 수 없다.
- 페이지가 커널 가상 메모리 영역에 존재한다.
- 읽기 전용 페이지에 쓰는 시도를 한다.
- 유효하지 않은 접근은 프로세스를 종료시키고 프로세스의 모든 자원을 해제한다.
- 페이지를 저장할 프레임을 획득한다.
- sharing을 구현한다면, 필요한 데이터가 이미 프레임에 있다. 이 경우 해당 프레임을 찾을 수 있어야 한다.
- 데이터를 파일 시스템이나 스왑에서 읽어오거나, 0으로 초기화하는 등의 방식으로 만들어서 프레임으로 가져온다.
- sharing을 구현한다면, 필요한 페이지가 이미 프레임 안에 있기 때문에 지금 단계에서는 별다른 조치가 필요하지 않다.
- 페이지 폴트가 발생한 가상 주소에 대한 페이지 테이블 엔트(PTE)가 물리 페이지를 가리키도록 지정한다.
threads/mmu.c
에 있는 함수를 사용하면 된다.
- “메모리 참조가 유효”이는 스택과 힙이 충분히 자라지 않은 경우 스택과 힙 사이에는 사용되지 않는 영역들이 존재한다는 말입니다. 이 사용되지 않은 영역을 참조하는 경우 ‘메모리가 유효하지 않다’고 표현합니다.
- 유저 가상 메모리의 모습을 생각하면 heap은 아래에서 위로, stack은 위에서 아래로 커지는 것을 떠올릴 수 있을 것입니다.
- Copy-on-Write이 메모리는 공유된 메모리이기 대문에 부모든 자식이든 쓰기를 하게 되면 나머지 프로세스에도 영향이 가게 된다. 따라서 fork를 하게 되면 고유된 해당 메모리에 대해 자식과 부모 모두 쓰기 권한이 무효화된다.이렇게 쓰기를 하려고 할 때 물리 메모리 복사되는 상황을 ‘쓰기 시 복사’ or Copy-on-Write라고 한다.
- 따라서 이 메모리에 쓰기 접근을 하면 페이지 폴트가 발생한다. 페이지 폴트 핸들러는 이 페이지를 다른 장소에 복사한 후에, 쓰기를 시도한 프로세스에 할당한 후 내용을 다시 작성한다. 이제 부모, 자식의 페이지 테이블 엔트리의 내용이 업데이트되고 쓰기 권한도 다시 생겨난다.
- fork()를 하면 자식에게 메모리 자체가 아닌 페이지 테이블만 복사하기 때문에 실제 물리 메모리를 자식과 부모가 공유하게 된다.
5. Managing the Frame Table
- 프레임 테이블은 각 프레임의 엔트리 정보(?)가 담겨있다.
- 프레임 테이블의 각 엔트리에는 현재 차지하고 있는 페이지에 대한 포인터(있는 경우라면), 그리고 당신의 선택에 따라 넣을 수 있는 기타 데이터들이 담겨있다.
- 프레임 테이블을 통해 핀토스는 이용 가능한 프레임이 없을때 방출할 페이지를 선택한다.
- ⇒ 프레임 테이블은 비어있는 프레임이 없을 때 쫓아낼 페이지를 골라줌으로써, Pintos가 효율적으로 eviction policy를 구현할 수 있도록 해준다.
- (결국, 물리 메모리도 한정되어있기 때문에 물리 메모리에 프레임이 꽉찼을 경우 가상 메모리의 페이지가 할당되기 위해서 현재 필요로 하지 않는(?) 프레임을 빼주는 것!)*
- 유저 페이지를 위해 사용된 프레임들은 palloc_get_page(PAL_USER)를 호출하여 user pool에서 획득된 것이어야 한다. (유저 페이지를 위해 사용된 프레임은 user pool에서 획득해야함.. 까지 정도로만 이해했음)
- kernel pool에서 할당했다가 예상치 못하게 테스트 케이스에서 실패하는 일을 막기 위해서는, 반드시 PAL_USER를 사용해야한다.
- 무조건 PAL_USER를 써서 kernel pool에서 가져오는 상황을 방지해야 한다.
- 프레임 테이블에 있어서 가장 중요한 기능은 사용하지 않는 프레임을 얻는 것이다. 프레임이 비어 있는 경우는 쉽다. 아무것도 비어있지 않는 경우에 특정 프레임은 해제되어야 한다.
- 만약 swap slot의 할당 없이 쫓아낼 수 있는 프레임이 없는데, 스왑 슬롯마저 꽉 차 있다면, 커널을 패닉시킨다. 실제 OS들은 이런 상황을 막거나 복구하기 위해 다양한 정책들을 적용하고 있습니다만, 그러한 정책들은 이 프로젝트의 범위를 벗어납니다.
- 프로세스의 방출(The process of eviction)은 대략적으로 아래의 단계를 따른다.
- 당신의 페이지 교체(재배치?) 알고리즘을 이용하여 방출할 프레임을 선택한다.
- 페이지 테이블의 “accessed”와 “dirty” 비트가 도움이 될 것이다.
- 해당 프레임을 참조하는 모든 페이지 테이블에서 프레임에 대한 참조를 제거한다.
- sharing을 구현하지 않은 경우, 해당 프레임을 참조하는 페이지는 항상 한 개만 존재해야한다.
- 필요한 경우 페이지를 파일 시스템이나 스왑에 write 한다. 방출된(evicted) 프레임은 다른 페이지를 저장하는데 사용될 수 있다.
- 당신의 페이지 교체(재배치?) 알고리즘을 이용하여 방출할 프레임을 선택한다.
5-1) Accessed and Dirty Bits
- x86-64 하드웨어는 각 페이지의 페이지 엔트리(PTE)에 있는 비트쌍을 통해 페이지 재배치 알고리즘 구현을 위한 도움을 제공한다. (흠 ?? 페이지 엔트리는 페이지에 있는거야? )
- 페이지에 read하거나 write 할 때, CPU는 페이지의 PTE에 있는 accessed 비트를 1로 설정한다.
- 페이지에 write 할 때, CPU는 페이지의 PTE에 있는 dirty 비트를 1로 설정한다.
- CPU는 절대 이 비트들을 0으로 되돌리지 않고, 대신 OS가 되돌릴 수 있다.
- 두 개 이상의 페이지가 동일한 프레임을 가리키는 경우를 주의해야한다. (aliases)
- aliased 프레임에 accessed 될 때, accessed와 dirty bit은 하나의 페이지 테이블 엔트리에서만 업데이트된다. (access에 쓰인 페이지에서만.)
- 핀토스에서 모든 유저 가상 페이지는 커널 가상 페이지에 aliased 되어있다. (그렇쳐~)
- 이러한 aliases에 대한 처리를 어떠한 방법으로든 해주어야 한다.
- 예를 들면, 당신의 코드는 aliase된 두 주소 모두의 accessed 비트와 dirty 비트를 확인하고 업데이트 할 수 있어야 한다.
- 또는, 커널이 오직 유저 가상 주소를 통해서만 유저 데이터에 접근하게 함으로써 이 문제를 피하게 할 수 있다.
- 이 경우가 아닌 다른 aliases는 sharing을 구현할때나 코드에 버그가 있는 경우에만 발생한다.
bool pml4_is_dirty (uint64_t *pml4, const void *vpage);
bool pml4_is_accessed (uint64_t *pml4, const void *vpage);
void pml4_set_dirty (uint64_t *pml4, const void *vpage, bool dirty);
void pml4_set_accessed (uint64_t *pml4, const void *vpage, bool accessed);
⇒ alias는 하나의 프레임을 두 개 이상의 페이지가 참조하는 것을 의미
⇒ 커널 영역은 항상 물리메모리 전체에 매핑되고, 유저 가상메모리 역시 물리 메모리에 매핑되기 때문에 결국 유저가상 페이지와 커널 가상 페이지는 항상 aliased 되어있는 것.
6. Managing the Swap Table
- 스왑 테이블은 사용 중인 스왑 슬롯과 빈 스왑 슬롯들을 추적한다.
- 프레임에 있는 페이지를 스왑 파티션으로 쫓아내기 위해서, 스왑 테이블은 미사용된 스왑 슬롯을 고를 수 있도록 해줘야한다.
- 페이지가 다시 읽혀서 돌아가거나, 페이지 주인인 프로세스가 종료되어 버릴 경우에는 스왑 테이블이 스왑슬롯을 free 해줄 수도 있어야한다.
- n-MB의 스왑 파티션을 포함하는
swap.dsk
라는 이름을 가진 디스크를 생성하기 위해서는,vm/build
경로에서pintos-mkdisk swap.dsk --swap-size=n
명령어를 사용하세요. - 그러면,
swap.dsk
는 pintos를 실행할 때 자동으로 추가 디스크로써 연결될 것입니다. - 또는,
--swap-size=n
명령어를 사용하여 n-MB의 임시 스왑 디스크를 일회성 실행을 위해 사용하도록 pintos에 명령할 수 있다. - 스왑 슬롯은 느긋하게 할당되어야 한다.
- eviction에 실제로 필요할 때만 할당되어야 한다는 말이다.
- 프로세스가 시작될 때 실행파일에서 데이터 페이지들을 읽고 스왑에 곧바로 쓰는 행위는 느긋하지 못한 행위이다. 특정 페이지를 저장하기 위해 스왑 슬롯이 예약되어서는 안 된다.
- swap slot은 특정 페이지를 저장하도록 예약되면 안된다.
- 스왑슬롯의 내용물이 프레임으로 읽혀돌아오면 그 때 스왑 슬롯을 free 해주면 된다.
6-1) Managing Memory Mapped Files
- 파일 시스템의 대부분은
read
,write
시스템 콜을 통해 수행된다. - 보조 인터페이스는
mmap
시스템 콜을 사용하여 파일을 가상 페이지로 “매핑”하는 것이다. - 그러면 프로그램이 파일 데이터에서 직접 메모리 인스트럭션(명령)을 사용할 수 있다. ???
foo
라는 파일이0X1000
바이트(4kB 또는 한 개 페이지)크기를 가졌다고 가정- 만약
foo
가0x5000
주소에서 시작하는 메모리에 매핑되어 있다면,0x5000 … 0x5fff
공간에 메모리 접근을 하면 그에 대응되는foo
파일의 바이트들에 접근될 것이다.- 메모리 주소 0x5000에 foo가 매핑 된다면 0x5000~0x5fff에 접근하면서 foo의 해당 바이트에 접근할 수 있게 된다.
#include <stdio.h>
#include <syscall.h>
int main (int argc UNUSED, char *argv[])
{
void *data = (void *) 0x10000000; /* Address at which to map. */
int fd = open (argv[1]); /* Open file. */
void *map = mmap (data, filesize (fd), 0, fd, 0); /* Map file. */
write (1, data, filesize (fd)); /* Write file to console. */
munmap (map); /* Unmap file (optional). */
return 0;
}
- 어느 메모리가 memory mapped file인지 추적할 수 있어야 한다.
- 이는 매핑된 영역의 페이지 폴트를 올바르게 처리하고 프로세스 내의 다른 세그먼트와 겹치지 않도록 하기 위해 필요하다.
- 이는 매핑된 영역에서 발생하는 페이지 폴트를 적절히 다루기 위해서, 그리고 매핑된 파일이 프로세스의 다른 조각(segement)들에 덮여쓰는 일이 없도록 하기 위해서 꼭 필요하다.
'SW사관학교 정글 > PintOS' 카테고리의 다른 글
[PintOS] Threads 다시 보다가 문맥교환(Context Switching) 이해하기 (0) | 2022.12.07 |
---|---|
[PintOS] Project 2: User Programs (Argument Passing, System Calls) (0) | 2022.12.06 |
[PintOS] AWS에서는 되는데 WSL에서 출력 결과가 터지는 경우 (2) | 2022.11.22 |
[PintOS] Project 2: User Programs 큰 그림 그리기 (1) | 2022.11.20 |
[PintOS] 인터럽트는 무엇이며, 왜 필요하며, 타이머 인터럽트의 역할은 무엇인가 (0) | 2022.11.18 |