본문 바로가기

CS(ComputerScience)

Pintos, Virtual Memory

728x90

Pintos, Virtual Memory

Pintos 가상메모리는 cpu가 RAM(Main Memory)보다 더 많은 메모리를 사용할 수 있게 RAM과 Disk Swap Area을 왔다갔다 해야 한다. 궁극적으로 프로세스에 사용할 수 있는 메모리 양을 효과적으로 늘릴 수 있으며 시스템에 물리적으로 존재하는 것보다 더 많은 메모리가 필요한 프로그램을 실행할 수 있다.

 

하지만, swap in/out 작업은 디스크 I/O의 도움이 필요하고 비용이 많이 든다.

그러므로 이 과정을 효과적으로 처리하기 위해(최소한으로 swap in/out 하기 위해) 여러 bit 정보를 이용한다.


 

[출처 : 정글 6기 3조 발표 자료]

 

 무한한 메모리를 실현하기 위한 방식은 크게 Virtual address space partitioning, page fault,  Page replacement방식이 있습니다.

 이 세가지 중 저희팀은 page replacement에 대해 집중해서 정리해보았습니다.

 page replacement는 page fault가 발생하고 필요한 페이지가 물리 메모리에 없을 때,

 운영 체제는 새로운 페이지를 받아오기 위한 공간을 만들기 위해 어떤 페이지를 교체할지 결정하는 방식입니다.

 가상 메모리는 디스크 스왑을 통해 실제의 DRAM 용량보다 큰 메모리 공간을 사용할 수 있지만, 

디스크 in/out 작업은 디스크의 물리적 특성과 작업의 복잡성 때문에 많은 시간과 비용이 소모됩니다. 

따라서 적절한 페이지 교체 알고리즘 사용하여 스왑 비용을 최소화하여 가능한 효율적인 교체가 이루어지도록 OS단에서 설계해야합니다.

 이를 위해 다양한  page replacement 알고리즘이 개발하였으며, 관해서 다음 짧게 소개하겠습니다.

 

🔎 디스크 i/O가 오래 걸리는 이유

디스크 I/O 작업이 많은 시간과 비용을 소모하는 이유는 디스크의 물리적 특성과 작업의 복잡성 때문입니다.

  1. 디스크는 회전하는 원판인 플래터와 팔을 이용하여 데이터에 접근합니다. 데이터를 읽거나 쓰려면 원판을 회전시켜 원하는 위치로 팔을 이동해야 합니다. 디스크의 회전 속도는 일반적으로 몇 천 RPM (Revolution Per Minute)입니다. 따라서 데이터를 접근하기 위해 원판을 회전시키고 위치를 찾아야 하는 시간이 필요하므로 상대적으로 오랜 시간이 소요됩니다.
  2. 디스크는 기계적인 장치로서 정확성과 신속성을 위해 정교한 메커니즘을 갖추고 있습니다. 데이터를 읽거나 쓰는 작업은 디스크 헤드를 이동시키고 원하는 위치에서 데이터를 읽거나 써야 합니다. 이 과정에서 작은 딜레이나 에러가 발생할 수 있으며, 이로 인해 작업에 소요되는 시간이 증가할 수 있습니다.
  3. 디스크 I/O 작업은 블록 단위로 이루어집니다. 하나의 블록은 일반적으로 여러 바이트의 데이터를 포함하며, 한 번에 한 블록을 읽거나 쓰게 됩니다. 이는 디스크와 메모리 간의 데이터 전송에 추가적인 시간이 필요하게 되어 작업 속도를 늦출 수 있습니다.
  4. 디스크는 다른 프로세스 또는 I/O 장치와 공유되는 자원입니다. 여러 프로세스나 장치가 동시에 디스크에 접근하려면 접근을 조율해야 합니다. 이를 위해 디스크 스케줄링 알고리즘이 사용되며, 이 역시 디스크 I/O 작업의 지연을 야기할 수 있습니다.

[출처 : 정글 6기 3조 발표 자료]

 

page replcement 알고리즘은 다음과 같이 크게 FIFO, LRU, LFU, random, optimal 등이 있습니다.

최근에는 LRU, LFU가 많이 사용되고, 이 외에도 기본적인 FIFO, 페이지를 무작위로 선택하는 random, 모든 페이지 접근 시점을 미리 알고 있다면 사용하는 optimal 방식이 있습니다.

 

저희는 이 알고리즘 중 LRU를 선택해서 구현했습니다. 

다음은 저희가 page replacement 및 LRU를 구현하면서 사용했던 페이지 엔트리관련 비트들에 대해 간략하게 설명드리겠습니다.

 

🔎 벨라디의 이상현상

 벨라디의 이상 현상은 페이지 교체 알고리즘 중 FIFO (First-In-First-Out) 알고리즘에서 발생할 수 있는 현상입니다. 이 현상은 페이지 부재가 빈번하게 발생하며, 페이지 프레임 수를 늘려도 성능이 개선되지 않는 상황을 의미합니다.

벨라디의 이상 현상은 다음과 같이 발생합니다. 가정해보겠습니다. 시스템에는 3개의 페이지 프레임이 있고, 주어진 시간 동안 다음과 같이 페이지가 요청되는 경우를 고려해봅시다:

  1. 페이지 A 요청
  2. 페이지 B 요청
  3. 페이지 C 요청
  4. 페이지 A 요청
  5. 페이지 D 요청
  6. 페이지 B 요청
  7. 페이지 E 요청

FIFO 알고리즘은 가장 오래된 페이지를 교체하는데, 위의 시나리오에서는 페이지 A, B, C가 먼저 할당되고, 이후에 다시 페이지 A가 요청되는 순서입니다. 이 경우, FIFO 알고리즘은 가장 오래된 페이지 A를 교체합니다. 하지만 페이지 A는 다시 요청되기 때문에 교체 후 다시 메모리에 로드됩니다. 이러한 상황에서는 페이지 부재가 빈번하게 발생하고, 페이지 프레임 수를 늘려도 성능이 개선되지 않는 현상이 벨라디의 이상 현상입니다.

이러한 이유로 FIFO 알고리즘은 벨라디의 이상 현상이 발생할 수 있는 단점을 가지고 있습니다. 이를 개선하기 위해 LRU (Least Recently Used) 또는 Optimal 알고리즘 등의 다른 페이지 교체 알고리즘을 사용할 수 있습니다.

 

🔎 FIFO

FIFO(First-In, First-Out) 알고리즘은 페이지 교체 알고리즘 중에서 가장 간단하고 기본적인 알고리즘입니다. FIFO 알고리즘은 먼저 메모리에 적재된 페이지를 먼저 교체하는 방식으로 동작합니다. 따라서 가장 오래된 페이지가 교체 대상이 됩니다.

FIFO 알고리즘은 구현이 간단하고 오버헤드가 적어서 많은 운영 체제에서 사용되었으며, 여전히 일부 시스템에서 사용되고 있습니다. 그러나 FIFO 알고리즘은 페이지의 접근 패턴을 고려하지 않기 때문에 페이지 교체의 품질이 다른 알고리즘들에 비해 낮을 수 있습니다. 특히, 페이지의 사용 빈도가 다른 페이지들과 크게 다를 경우 교체 효율이 저하될 수 있습니다.

실제로 FIFO 알고리즘은 최근에 사용되는 더 효율적인 페이지 교체 알고리즘들에 비해 상대적으로 사용량이 줄어들었습니다. LRU(Least Recently Used), LFU(Least Frequently Used), OPT(최적 교체) 등의 알고리즘이 FIFO에 비해 더 많이 사용되고 있습니다. 그러나 FIFO 알고리즘은 간단하고 예측 가능한 동작으로 인해 몇몇 특정 상황에서 성능이 좋을 수 있습니다. 따라서 특정 시스템이나 요구 사항에 따라 FIFO 알고리즘이 선택될 수 있습니다.

 

🔎 LRU

LRU(Least Recently Used)는 페이지 교체 알고리즘 중에서 가장 널리 사용되는 알고리즘 중 하나입니다. LRU 알고리즘은 페이지의 최근 사용 여부를 기반으로 페이지를 교체하는 방식으로 동작합니다.

LRU 알고리즘은 메모리에 적재된 페이지들 중에서 가장 오랫동안 사용되지 않은 페이지를 교체하는 원리입니다. 페이지에 접근이 발생할 때마다 해당 페이지를 최신으로 갱신하여 최근에 사용된 페이지를 추적합니다. 교체가 필요한 상황에서는 가장 오래전에 사용된 페이지를 선택하여 교체합니다.

LRU 알고리즘은 페이지의 접근 패턴을 고려하여 교체를 수행하기 때문에 교체의 정확성과 성능 면에서 우수한 알고리즘으로 알려져 있습니다. 최근에 사용된 페이지들은 교체되지 않고 메모리에 유지되는 경향이 있기 때문에 Locality(지역성) 원리에 잘 부합합니다. 일반적으로 실제 시스템에서도 LRU 알고리즘이 많이 사용되고 있습니다.

하지만 LRU 알고리즘은 페이지 접근 패턴을 추적하기 위해 추가적인 자료 구조와 연산이 필요하므로 구현 비용이 높을 수 있습니다. 또한, 페이지 접근 패턴이 예측하기 어려운 경우에는 교체 성능이 저하될 수 있습니다. 이러한 이유로 실제 시스템에서는 LRU의 근사 알고리즘인 Clock 알고리즘 등이 사용되기도 합니다.

 

🔎 LFU

LFU(Least Frequently Used)는 페이지 교체 알고리즘 중 하나로, 페이지의 사용 빈도를 기반으로 페이지를 교체하는 방식입니다.

LFU 알고리즘은 각 페이지의 사용 빈도를 추적하여 가장 적게 사용된 페이지를 교체하는 원리로 동작합니다. 페이지에 접근이 발생할 때마다 해당 페이지의 사용 빈도를 증가시킵니다. 교체가 필요한 상황에서는 가장 적게 사용된 페이지를 선택하여 교체합니다.

LFU 알고리즘은 페이지의 사용 빈도를 정확하게 추적하여 교체를 수행하기 때문에 최근에 적게 사용된 페이지들이 교체되는 경향이 있습니다. 이는 특정 페이지가 장기간 사용되지 않을 경우 교체되는 것을 보장합니다.

하지만 LFU 알고리즘은 페이지의 사용 빈도를 추적하기 위한 추가적인 자료 구조와 연산이 필요하므로 구현 비용이 높을 수 있습니다. 또한, 페이지 접근 패턴이 예측하기 어려운 경우에는 교체 성능이 저하될 수 있습니다.

 

🔎 Optimal

Optimal 페이지 교체 알고리즘은 가장 이상적인 페이지 교체 알고리즘으로도 알려져 있습니다. Optimal 알고리즘은 페이지 교체 시 가장 오랫동안 사용되지 않을 페이지를 교체하는 방식입니다.

Optimal 알고리즘은 현재 시점에서 앞으로 가장 오랫동안 사용되지 않을 페이지를 선택하여 교체합니다. 이를 위해 알고리즘은 모든 페이지 접근 시점을 미리 알고 있어야 합니다. 모든 페이지 접근 시점을 미리 알고 있다면, 가장 오랫동안 접근이 없을 것으로 예상되는 페이지를 교체합니다.

Optimal 알고리즘은 이론적으로 가장 효율적인 페이지 교체 알고리즘이지만, 실제로 구현하기에는 매우 어렵고 비현실적입니다. 이는 알고리즘이 앞으로 발생할 페이지 접근 시점을 미리 알아야 하기 때문입니다. 실제 운영 체제에서는 앞으로 발생할 페이지 접근을 예측하기 어렵기 때문에 Optimal 알고리즘을 실제로 사용하기는 어렵습니다.

 

🔎 RANDOM

Random 페이지 교체 알고리즘은 페이지를 무작위로 선택하여 교체하는 방식입니다. 각 페이지에 대해 교체될 확률이 동일하게 유지되며, 이전 페이지 접근 기록이나 사용 빈도에 대한 정보를 고려하지 않습니다.

Random 알고리즘은 간단하고 직관적이지만, 페이지 교체의 품질을 보장하지 않습니다. 이 알고리즘은 무작위성에 의존하기 때문에 어떤 페이지가 교체될지 예측할 수 없습니다. 때문에 최적의 페이지 교체를 보장하지 않을 수 있으며, 다른 알고리즘들보다 성능이 낮을 수 있습니다.

Random 알고리즘은 간단하고 구현하기 쉬우며, 페이지 접근 패턴에 대한 정보가 없는 경우나 균등한 확률 분포를 원하는 경우에 사용될 수 있습니다. 그러나 실제로는 페이지 접근 패턴을 고려하는 다른 알고리즘들이 더 나은 성능을 보이는 경우가 많습니다.

요약하자면, Random 알고리즘은 페이지를 무작위로 선택하여 교체하는 방식입니다. 간단하고 구현하기 쉽지만, 최적의 페이지 교체를 보장하지 않고 다른 알고리즘들보다 성능이 낮을 수 있습니다.


[출처 : 정글 6기 3조 발표 자료]

Valid bit는 페이지 테이블 엔트리에서 사용되는 비트로, page가 물리메모리와 매핑되어있다면 1, 매핑되어있지 않다면 0으로 표시되어어야 합니다.

참고로 핀토스에서는 PTE_P라는 변수명으로 present  bit로 표시되어 있습니다.

 

페이지 폴트는 present bit가 0인 페이지에 접근하는 과정이므로,  Paging_init함수와 mmu.c 파일의 pml4와 관련된 함수 등의 가상주소와 물리주소를 매핑하는 모든 과정에서 사용됩니다.



PTE_P 사용함수 : paging_init, mmu.c에엇 pml4관련 모든 함수

- present bit가 0인 페이지에 접근하는 과정이 페이지 폴트!

 

1. Paging_init : main 함수에서, 메모리 시스템을 초기화 할 때 사용되는 함수.

    - palloc_init 함수가 메모리 크기를 결정하면, paging_init이 결정된 크기만큼 페이지 테이블을 초기화하고 메모리 매핑을 수행.

    - 이 때, PTE_P를 사용하여 페이지 테이블 엔트리가 유효함을 나타냄.

 

2. mmu.c 파일 → pml4 관련 모든 함수 : 마찬가지로 pml4 관련 모든 함수들에서 페이지 테이블 엔트리의 유효성을 표현할 때 valid bit를 사용.

 

- valid bit는 페이지 테이블에서 가상주소와 물리주소를 매핑하는 모든 과정에서 사용됨. 


[출처 : 정글 6기 3조 발표 자료]

Access bit는 페이지 교체 알고리즘에서 최근에 읽기 또는 쓰기 작업에 의해 접근되었는지 여부를 확인하기 위한 비트입니다.

물리메모리의 frame이 이미 모두 매핑되어있다면 LRU정책에 맞춰 가장 오랫동안 사용되지 않은 페이지와 매핑된 frame을 swap out을 통해 희생시키고 실행시킬 frame을 swap in해줘야합니다.

 핀토스에서는  vm_get_frame, vm_evict_frame, vm_get_victim이 차례로 해당 로직에 관여하며,  vm_get_victim함수에서는 for문으로 frame_list를 차례로 돌면서 pml4_is_accessed함수를 통해 이미 접근한 기록이 있는 pte는 비트를 0으로 바꾸면서 지나가고, 접근 기록이 없는 pte가 나왔을 때 해당 페이지를 victim(희생양)으로 선택하여 리턴함으로써 LRU가 구현되도록 했습니다.




[ PTE_A 사용 함수vm_get_frame  → vm_evict_framevm_get_victim ]

 

1. vm_get_frame : 물리메모리에서 페이지를 할당받는 함수. 이 때 모든 물리메모리의 프레임이 사용 중인 경우, 제거할 frame을 정해야 함. (vm_evict_frame 호출)

2. vm_evict_frame : 삭제할 프레임을 결정한 후, 실제로 페이지를 하드디스크로 스왑 아웃. 여기서 삭제할 프레임을 결정하기 위해 다시 vm_get_victim을 호출

3. vm_get_victim : 삭제할 프레임을 결정하는 함수!!

    - pml4_is_accessed : 주어진 pml4와 가상주소를 기반으로 해당 가상페이지가 접근(access)되었는지 확인하는 함수. PTE가 존재하고, 해당 엔트리의 accessed bit가 설정되어 있다면(1) true 반환

    - pml4_set_accessed : 해당 가상페이지의 accedssed bit를 설정하거나 해제하는 함수

    - vm_get_victim에서 Accessed Bit를 사용해 페이지 교체 알고리즘을 구현할 수 있음. 우리 팀은 LRU 방식으로 구현함. for문으로 frame_list를 차례로 돌면서 사용(접근)하지 않았던 페이지를 골라냄. pml4_is_accessed로 이미 접근한 기록이 있는 pte는 비트를 0으로 바꾸면서 지나가고, 접근 기록이 없는 pte가 나왔을 때 해당 페이지를 victim(희생양)으로 선택하여 리턴함.


[출처 : 정글 6기 3조 발표 자료]

Dirty bit(더티 비트)는 페이지의 수정 여부를 나타내는 비트입니다.

앞서 말씀드렸듯이 DISK in/out을 하는데에는 많은 cost가 소비됩니다. 이를 줄이기 위해서 페이지가 수정되었을 때만 swap out이 진행되도록 하는는 비트입니다.

관련된 함수는 do_munmap,  file_backed_swap_out함수로 dirty bit가 1일때 수정된 내용을 디스크에 업데이트하고 더티비트를 0으로 초기화 합니다.

 

이렇게 present bit, access bit, dirty bit를 통해 page replacement와 LRU를 구현한 내용을 짧게나마 정리해서 말씀드렸습니다.

저희조가 준비한 내용은 여기까집니다. 설명 들어주셔서 감사합니다.




[ PTE_D 사용 함수do_munmap  & file_backed_swap_out ]

 

1. do_munmap : 주어진 가상주소 범위의 메모리 매핑을 해제하는 함수. 

    - while문을 사용해, PGSIZE씩 addr를 늘려가면서, 각 addr에 대응되는 페이지의 uninit 필드에 저장된 aux를 가져옴(container). (가져온 구조체에는 페이지와 관련된 추가 정보들이 저장되어 있음.)

    - pml4_is_dirty : 만약 페이지에 대응하는 pml4의 엔트리가 존재하고, Dirty bit가 설정된 경우(수정된 이력이 있는 경우), 

    - file_write_at : 수정된 내용을 디스크에 업데이트하고,

    - pml4_set_dirty : 더티 비트를 0으로 초기화해 줌.

    - pml4_clear_page : 이 후, 현재 프로세스의 pml4에서 해당 가상주소에 대응하는 PTE를 제거! 

 

2. file_backed_swap_out : File_backed page를 스왑 아웃하는 함수.

    - 주어진 페이지의 uninit 필드에 저장된 aux(container) 를 가져온 후, 

    - pml4_is_dirty 함수로 해당 페이지의 dirty bit를 확인하고, 

    - file_write_at 함수로 수정된 페이지의 내용을 파일에 기록함. 

    - pml4_set_dirty 함수로 pml4 엔트리의 더티 비트를 0으로 초기화

    - pml4_clear_page 함수로 현재 프로세스의 pml4에서 해당 가상주소에 대응하는 PTE를 제거! 

 

[ 참고 ]

- Dirty Bit : 페이지 변경 여부 저장하는 비트. 변경될 때마다 해당 비트는 1이 되고, 디스크에 변경 내용을 기록하면 0으로 초기화 됨.

- do_munmap 과 file_backed_swap_out에서 더티 비트는 비슷한 플로우로 사용됨.

 

- 어떤 페이지가 Swap Out 될 때, 해당 페이지가 수정되지 않았다면 스왑 아웃할 필요가 없음. 동일한 내용이 Disk 어딘가에 있기 때문임.

  따라서, 페이지의 수정 여부를 알 수 있다면, 페이지를 교체할 때 스왑 아웃 시간을 절약할 수 있음. (Swap In 만 해주면 되기 때문)

  이 때, 페이지의 수정여부를 알려주는 값이 바로 Dirty Bit임! 




🔎 mmap system call

file + memory allocating 즉, 메모리 할당용 call

만약, file없이 memory만 할당한다면, anonymous page가 되는 거임!

만약, 두개의 프로세스가 같은 파일에 대해 mapping한다면, shared memory가 되는 거임!

'CS(ComputerScience)' 카테고리의 다른 글

Pintos File System  (0) 2023.05.31
tiny 웹서버 구현  (0) 2023.04.19
Network, 통신 Model  (0) 2023.04.19
명시적 가용 리스트 Explicit Free List  (0) 2023.04.13
묵시적 가용 리스트 Implicit Free List  (1) 2023.04.12