pintos

[WIL]Project3: Virtual Memory

장영 2023. 10. 11. 23:20

Project3: Virtual Memory

당신의 OS는 여러 개의 쓰레드를 알맞게 동기화해서 적절히 핸들링할 수 있고, 여러개의 유저 프로그램을 한번에 로드할 수 있다. 그러나, 돌릴 수 있는 프로그램의 개수와 사이즈는 머신의 메인 메모리 크기에 맞춰 제한되어있다.

이번 과제를 통해 무한의 메모리의 환상을 만들어서 그 제한을 제거해보자.

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_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로 변환된다.
  • include/vm/anon.h, vm/anon.c
    • anonymous page를 위한 기능을 제공한다. (vm_type = VM_ANON).
  • include/vm/file.h, vm/file.c
    • file-backed page를 위한 기능을 제공한다. (vm_type = VM_FILE).
  • 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, frame, page table, swap slots을 소개한다. 이 파느는 꼼꼼하게 봐야함
  • 메모리와 저장소에 관한 몇가지 용어도 같이 봐야한다. 프로젝트 2 깃북도 참고

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() in thread/mmu.c )를 바꿈으로써 유저 가상 주소 공간 또한 바꿔준다.
  • 스레드 구조체는 하나의 프로세스의 페이지 테이블을 가리키는 포인터를 가지고 있다. ⇒ *pml4
  • 커널 가상 메모리는 전역적이다.
  • 커널 가상 메모리는 어떠한 유저 프로세스 또는 커널 스레드가 cpu제어권을 획득해 running인지 관계없이 항상 같은 방식으로 매핑된다. ⇒ 모든 유저 프로세스의 vm의 kernel vm은 항상 동일하다.
  • 핀토스에서 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에서는 유저 스택의 크기는 가변적이게 된다. ⇒유저 스택의 크기를 가변적으로 만들면 됨.
  • 핀토스에서 코드 세그먼트는 가상 메모리 0x4000000에서 시작하고, 대략 주소 공간의 바닥에서 128MB만큼 떨어져 있다.

2-3) Pages

  • ‘페이지’는 가상 페이지라고도 불린다.
  • ‘페이지’는 4096바이트 ⇒ (4kib = 페이지 크기)의 길이를 가지는 가상 메모리의 연속된 영역
  • page는 가상 메모리를 일정한 크기(4kib) 로 나눈 블록이다라고 생각하면 된다.
  • 페이지는 항상 page-alingned되어 있어야한다. ⇒정렬
    • 즉 , 각 페이지는 페이지 크기로 균등하게 나누어지는 가상 주소에서 시작해야한다.
    • 시작 위치는 항상 page size로 나누어 떨어져야한다.
  • 64비트 가상 주소 위에서 하위 12비트는 page offset이다. ⇒해당 페이지 내에서 특정 데이터 아이템의 오프셋 또는 위치를 가리킴
  • 상위 비트들은 페이지 테이블 페이지의 인덱스로 사용된다.
    • 예를 들어, 만약 가상 메모리 주소가 "0x12345678"이라면, "0x1234"는 페이지 번호를 나타내고 "0x5678"은 해당 페이지 내에서의 오프셋을 나타내는 페이지 오프셋입니다. 페이지 오프셋을 통해 시스템은 정확한 메모리 위치를 식별하고 데이터를 읽거나 쓸 수 있습니다.
  • 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
  • 커널 페이지들은 전역적이고, 어떤 스레드 또는 프로세스가 실행중이어도 동일한 위치가 유지된다.
  • 커널은 유저와 커널 페이지를 모두 접근할 수 있지만, 유저 프로세스는 스스로의 유저 페이지들에만 접근이 가능하다.

2-4) frames

  • 물리 프레임 또는 페이지 프레임이라고도 불리는 프레임은 물리 메모리상의 연속적인 공간을 의미한다.
  • frame은 물리 메모리를 일정한 크기로 나눈 블록이다.
  • 페이지와 동일하게, 프레임은 페이지 사이즈여야하고 페이지 크기에 정렬되어 있어야한다.(페이지 사이즈 = 프레임 사이즈 = 4KiB)
  • 페이지와 동일하게 프레임 또한 page-size를 가지고 page-aligned되어야 한다.
  • 64비트 물리 주소는 아래와 같이 frame 넘버와 frame 오프셋으로 나누어 진다.
			12 11         0
    +-----------------------+-----------+
    |      Frame Number     |   Offset  |
    +-----------------------+-----------+
              Physical Address
  • x86-64는 물리 메모리에 있는 메로리에 직접적으로 접근하는 방법을 제공하지 않는다.
  • 핀토스는 커널 가상 메모리를 물리 메모리에 직접 매핑하여 이 작업을 수행한다.
    • 커널 가상 메모리의 첫 번째 페이지는 물리 메모리의 첫 번째 프레임에 매핑되고, 두번째 페이지는 물리 메모리의 두 번째 프레임에 매핑된다.
    • 커널 가상 메모리를 통해 프레임에 접근할 수 있다.
  • 핀토스는 물리적 주소커널 가상주소 사이의 변환을 수행하는 함수를 제공한다.
    • 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에 있다.
    • 왼쪽의 가상 주소는 페이지 번호와 오프셋으로 구성되어 있다.
    • 페이지 테이블은 페이지 번호를 프레임 번호로 변환하며, 프레임 넘버는 오른쪽에 보이는 것처럼 물리주소를 획득하기 위한 미수정된 오프셋과 결합외어 있다. 오프셋은 항상 똑같다.아래의 그림은 페이지와 프레임의 관계를 나타낸다.
                          +----------+
         .--------------->|Page Table|-----------.
        /                 +----------+            |
        |   12 11 0                               V  12 11 0
    +---------+----+                         +---------+----+
    | Page Nr | Ofs|                         |Frame Nr | Ofs|
    +---------+----+                         +---------+----+
     Virt Addr   |                            Phys Addr    ^
                  \_______________________________________/

2-6) Swap Slots

  • 스왑 슬롯은 스왑 파티션 내에 디스크 공간에 있는 페이지 크기의 영역 ⇒ swap은 disk의 한 영역이다.
  • 하드웨어적 제한들로 인해 배치가 강제되는 것(정렬)이 프레임에서 보단 슬롯에서 더 유연한 편이지만, 정렬한다고 해서 별다른 부정적인 영향이 생기는 건 아니기 떄문에 스윕 슬롯은 페이지 크기에 정렬하는 것이 좋다.

 

3. Resource Management Overview 자원 관리 개요

다음의 자료 구조에 대해 설계 및 구현을 해야한다.

3-1) page table 페이지 테이블: 가상 주소를 물리 주소로 바꿔주는 역할을 하는 자료구조

  • 위에서 나온 페이지 테이블을 말한다. 페이지 테이블은 페이지 번호를 프레임 번호로 변환하며, 오프셋은 동일

3-2) Supplemental page table 보조 페이지 테이블 : 페이지 테이블에서 부족한 정보를 채워주기 위해 프로세스마다 존재하는 table을 보조해서, 페이지 폴트 핸들링이 가능하도록 해준다.

  1. 페이지 폴드 정보를 가져온다.
  2. 새로운 프레임을 할당한다
  3. 실행 파일에서 읽는다.
  4. 정보를 업데이트한다.
  5. 새로운 페이지를 설치한다.

3-3) Frame table 프레임 테이블:  현재 사용 중인 frame들을 관리하기 위한 table / 물리 프레임의 방출 정책(eviction policy)를 구현하는 것을 가능케한다.

3-4) Swap table 스왑 테이블: swap 영역 관리를 위한 table / swap slots의 사용을 추적한다.

3-5) Choices of implementation (Performance perspective) 성능 관점에서 구현 선택

  • 배열, 리스트, 비트맵, 해시 테이블 중 하나를 선택해서 구현할 수 있다.
    • 배열은 가장 간단한 방법이지만 배열이 희박하게 채워지면 메모리가 낭비됨
    • 리스트 또한 간단하지만 긴 리스트를 순회하는 것은 시간 낭비
    • 배열과 리스트 모두 크기를 다시 지정할 수 있지만 리스트가 중간에 삽입, 실제를 더 효율적으로 지원한다.
  • 핀토스는 배트맵 자료구조 또한 제공한다.(lib/kernel/bitmap.c, include/lib/kernel/bitmap.h)
  • 핀토스는 또한 해시 테이블 자료구조를 포함하고 있다. 핀토스 해시 테이블은 다양한 테이블 크기에서의 삽입과 삭제를 효율적으로 지원한다.⇒ 이번 프로젝트에서 해시테이블 사용할것 같다.
  • 더 복잡한 자료구조는 사용하지 말자

4. Managing the Supplemental Page Table - 구현 관려 내용

  • SPT( supplemental page table, 보조테이블)이란?
    • pml4(페이지 테이블)에서 부족한 정보를 채워주기 위한 프로세스마다 존재하는 테이블
  • 왜 SPT를 사용해야 하는가?
    1. page fault시 SPT에서 오류가 발생한 page를 조회하여 데이터가 있어야할 위치를 확인
    2. process가 종료될 때 kernel 이 SPT를 참조하여 어떤 resource를 free할 것인지를 결정
  • 권영진 교수님 강의 자료에서의 SPT 정의
    • SPT ⇒ 아래의 보조 데이터들을 담고있는, 프로세스마다 존재하는 자료구조
    1. 각각의 페이지에 대해서 데이터가 존재하는 곳(frame, disk, swap 중 어디 존재하는지)
    2. 이에 상응하는 커널 가상 주소를 가리키는 포인터 정보
    3. page와 관련된 추가 정보(activate인지 inactivate인지 등등)

4-1) Organization of Supplemental Page Table

  • SPT는 하고싶은대로 구현하면 된다.
  • 두 가지 방식의 구조를 가질 수 있다. 세그먼트 기반, 페이지 기반 방식
    • 여기서 말하는 세그먼트는 연속적인 페이지들의 그룹을 말한다. ⇒실행 파일 또는 메모리 매핑 파일을 포함하는 메모리영역
  • advanced에 도전하고 싶은 경우 페이지 페이지 테이블을 수정하여 spt를 포함하도록 구현
    • 이떄는 thread/mmu.c를 수정하면 된다.

4-2) Handling page fault 페이지 폴트 다루기

  • SPT핵심 유저는 페이지 폴트 핸들러이다.
  • 프로젝트 2에서는 페이지 폴트는 항상 커널이나 유저 프로그램의 버그를 의미했다.
  • 프로젝트 3부터는 페이지 폴트는 그 페이지가 파일이나 스왑 슬롯에서 가져와야 함을 나타낸다.
  • 더 복잡한 페이지 폴트 핸들러를 구현하여 이 경우를 처리할 수 있도록 하자.
  • • userprog/exception.c의 page_fault()는 vm/vm.c에 있는 당신이 구현한 vm_try_handle_fault()를 호출한다.
  • 구현해야 하는 기능들은 아래와 같다.
  1. SPT에서 페이지 폴트가 발생한 페이지를 찾는다. 메모리가 참조가 유효하면 SPTE(SPT ENTRY)를 사용해서 페이지에 들어갈 데이터를 찾는다.
    1. 데이터는 파일 시스템에 있거나, 스왑 슬롯에 있거나, 단순히 0으로만 이루어진 페이지 일 수 있다.
    2. 만약 sharing(i.e , copy-on-write)을 구현한다면 페이지의 데이터는 페이지 프레임에 있지만 페이지 테이블에는 없을 수도 있다.
    3. 만약 SPT이 다음과 같은 정보를 보여주고 있다면 그 접근은 유효하지 않은 접근이다.
      1. 유저 프로세스가 접근하려는 주소에서 어떤 데이터도 얻을 수 없다.
      2. 페이지가 커널 가상 메모리 영역에 존재한다.
      3. 읽기 전용 페이지에 쓰는 시도를 한다.
    4. 유요하지 않은 접근은 프로세스를 종료시키고 프로세스의 모든 자원을 해제한다.
  2. 페이지를 저장할 프레임을 획득한다.
    1. sharing을 구현한다면, 필요한 데이터가 이미 프레임에 있다. 이 경우 해당 프레임을 찾을 수 있어야한다.
  3. 데이터 파일 시스템이나 스왑에서 읽어오거나, 0으로 초기화하는 등의 방식으로 만들어서 프레임으로 가져온다.
    1. sharing을 구현한다면, 필요한 페이지가 이미 프레임 안에 있기 때문에 지금 단계에서는 별다른 조치가 필요하지 않다.
  4. 페이지 폴트가 발생한 가상 주소에 대한 페이지 테이블 엔트(PTE)가 물리 페이지를 가리키도록 지정한다.
    1. threads/mmu.c에 있는 함수를 사용하면 된다.
  • “메모리 참조가 유효” 이는 스택과 힙이 충분히 자라지 않은 경우 스택과 힙 사이에는 사용되지 않는 영역들이 존재한다는 말, 이 사용되지 않은 영역을 참조하는 경우 “메모리가 유효하지 않다”고 표현한다.
  • 유저 가상 메모리의 모습을 생각하면 heap은 아래에서 위로, stack은 위에서 아래로 커지는 것을 떠올릴 수 있다.
  • copy-on-write이 메모리는 공유된 메모리이기 때문에 부모든 자식이든 쓰기를 하게 되면 나머지 프로세스에도 영향이 가게된다. 따라서 fork를 하게 되면 고유된 해당 메모리에 대해 자식과 부모 모두 쓰기 권환이 무효화된다. 이렇게 쓰기를 하려고 할 때 메모리 복사되는 상황을 “쓰기 시 복사” or copy-on-write라고 한다.
  • 따라서 이 메모리에 쓰기 접근을 하면 페이지 폴트가 발생한다. 페이지 폴트 핸들러는 이 페이지를 다른 장소에 복사한 후에, 쓰기를 시도한 프로세스에 할당한 후 내용을 다시 작성한다. 이제 부모, 자식의 페이지 테이블 엔트리의 내용이 업데이트 되고 쓰기 권한도 다시 생겨난다.
  • fork()를 하면 자식에게 메모리 자체가 아닌 페이지 테이블만 복사하기 때문에 실제 물리 메모리를 자식과 부모가 공유하게 된다.

5. Managing the Frame Table

  • 프레임 테이블은 각 프레임의 엔트리 정보가 담겨있다.
  • 프레임 테이블의 각 엔트리에는 현재 차지하고 있는 페이지에 대한 포인터, 그리고 당신의 선택에 따라 넣을 수 있는 기타 데이터들이 담겨있다.
  • 프레임 테이블을 통해 핀토스는 이용 가능한 프레임이 없을때 방출할 페이지를 선택한다.
    • ⇒프레임 테이블은 비어있는 프레임이 없을때 쫓아낼 페이지를 골라줌으로써, 핀토스가 효율적으로 eviction policy를 구현할 수 있도록 해준다.
    • eviction policy : 이 정책은 주로 데이터가 메모리에서 삭제될 때 어떤 데이터가 삭제되어야 하는지 결정하는 데 사용
  • 결국, 물리메모리도 한정되어 있기 때문에 물리 메모리에 프레임이 꽉찼을 경우 가상 메모리의 페이지가 할당되기 위해 현재 필요하지 않는 프레임을 빼주는것
  • 유저 페이지를 위해 사용된 프레임들은 palloc_get_page(PAL_USER)를 호출하여 user pool에서 획득된 것이어야 한다. (유저 페이지를 위해 사용된 프레임은 user pool에서 획득해야함)
    • kernel pool에서 할당 했다가 예상치 못하게 테스트 케이스에서 실패하는 일을 막기 위해서는 , 반드시 PAL_USER를 사용해야한다.
    • 무조건 PAL_USER을 써서 kernel pool에서 가져오는 상황을 방지해야한다.
  • 프레임 테이블에 있어서 가장 중요한 기능은 사용하지 않는 프레임을 얻는것이다. 프레임이 비어있는 경우는 쉽다. 아무것도 비어있지 않은 경우에 특정 프레임은 해제되어야 한다.
  • 만약 swqp slot의 할당 없이 쫓아낼 수 있는 프레임이 없는데, 스왑 슬롯 마저 꽉 차있다면, 커널을 패닉시킨다. 실제 os들은 이런 상황을 막거나 복구하기 위해 다양한 정책들을 적용하고 있다만, 그러한 정책들은 이 프로젝트 범위를 벗어난다.
  • 프로세스 방출은 대략적으로 아래의 단계를 따른다.
    1. 당신의 페이지 교체 알고리즘을 이용하여 방출할 프레임을 선택한다.
      1. 페이지 테이블의 “accessed”와 “dirty” 비트가 도움이 될 것이다.
    2. 해당 프레임을 참조하는 모든 페이지 테이블에서 프레임에 대한 참조를 제거한다.
      1. sharing을 구현하지 않은 경우, 해당 프레임을 참조하는 페이지는 항상 한 개만 존재해야한다.
    3. 필요한 경우 페이지를 파일 시스템이나 스왑에 write한다. 방출된 프레임은 다른 페이지를 저장하는데 사용될 수 있다.

5-1) Accessed and Dirty Bits

  • x86-64 하드웨어는 각 페이지의 페이지 엔드리(PTE)에 있는 비트쌍을 통해 페이지 재배치 알고리즘 구현을 위한 도움을 제공한다
  • 페이지에 read하거나 write할 때, CPU는 페이지 PTE에 있는 비트를 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.disk 라는 이름을 가진 디스크를 생성하기 위해서는, vm/build 경로에서 pintos-mkdisk swap.dsk --swap-size=n 명령어를 사용하세요.
  • 그러면, swap.dsk는 pintos를 실행할 때 자동으로 추가 디스크로써 연결될 것입니다.
  • 또는, -swap-size=n 명령어를 사용하여 n-MB의 임시 스왑 디스크를 일회성 실행을 위해 사용하도록 pintos에 명령할 수 있다.
  • 스왑 슬롯은 느긋하게 할당되어야 한다.
    • eviction에 실제로 필요할 때만 할당되어야 한다는 말이다.
  • 프로세스가 시작될 때 실행파일에서 데이터 페이지들을 읽고 스왑에 곧바로 쓰는 행위는 느긋하지 못한 행위이다. 특정 페이지를 저장하기 위해 스왑 슬롯이 예약되어서는 안된다.
    • 스왑 슬롯은 특정 페이지를 저장하도록 예약되면 안된다.
  • 스왑 슬롯의 내용물이 프레임으로 읽혀 돌아오면 그 때 스왑 슬롯을 free해주면 된다.

6-1) Managing Memory Mapped Files

  • 파일 시스템의 대부분은 read, write 시스템 콜을 통해 수행된다.
  • 보조 인터페이스는 mmap 시스템 콜을 사용하여 파일을 가상 페이지로 매핑 하는것이다.
  • 그러면 프로그램이 파일 데이터에서 직접 메모리 인스트럭션을 사용할 수 있다.
  • foo 라는 파일이 0x1000바이트 (4kb 또는 한개 페이지) 크기를 가졌다고 가정
  • 만약 foo가 0x5000 주소에서 시작하는 메모리에 매핑되어 있다면, 0x5000 … 0x5fff 공간에 메모리 접근을 하면 그에 대응되는 foo 파일의 바이트들에 접근될 것이다.
    • 메모리 주소는 0x5000에 foo가 매핑 된다면 0x5000~ 0x5ffff에 접근하면서 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인지 추적할 수 있어야한다.
    • 이는 매핑된 영역의 페이지 폴트를 올바르게 처리하고 프로세스 내의 다른 세그먼트와 겹치지 않도록 하기 위해 필요하다.
  • 이는 매핑된 영역에서 발생하는 페이지 폴트를 적절히 다루기 위해서, 그리고 매핑된 파일이 프로세스의 다른 조각(세그먼트)들에 덮여 쓰는 일이 없도록 하기 위해서 꼭 필요하다.

'pintos' 카테고리의 다른 글

[WIL] Copy On Write  (1) 2023.10.24
[WIL] PROJECT1 - Alarm Clock  (0) 2023.10.03
[PROJECT 1 : THREADS]과제 설명  (0) 2023.09.21