운영체제 8장- 2. Virtual Memory


어떻게하면 page fault를 적게 만들수 있을가 하는것이 가상 기억장치 성능에 중요한 관건
어떻게 가져올것인가 (Fetch Policy)
- 필요할때마다 가져온다
- 미리 가져온다

가져온 페이지를 메인메모리 어디에 둘것인가(placement policy)
- 메인메모리 부족시 기존 페이지를 내보내고 빈곳에 두어야 하는데 어떤것을 내보낼 것인가
미래를 볼수있다면 - 최적 optimal
- LRU - 최근에 덜 사용된것이 미래에도 덜 사용될것이다(가장 좋은 방법)
- FIFO - 먼저 들어온것이 빨리 나갈것이다
- Clock - 버츄얼 메모리는 1클럭 안에 되어야, 이것이 복잡하면 안된다. LRU를 간편하게 만든것

victim, 희생되는 페이지를 버퍼에 넣게 되는데, 버퍼를 어떻게 관리할 것인가
- 앞으로 쓰이지 않을 페이지를 내보내는 방법도 있지만, 더 직접적인 방법으로 
프로세스별로 필요한 페이지의 개수를 관리한다면, locality를 충분히 수용할 만큼 유지가 된다면, 페이지 fault수를 줄일수 있다.

working set page frame들을 resident set 이라고 하고(resident set management)
- 그것을 고정되게 관리
- 가변되게 관리하는 방법

resident set을 제거할때, 
- 한 프로세스 내에 속한것에서 희생자를 찾는것 (global)
- 전체 영역에서 희생자를 찾는것 (local)

data가 수정되는 페이지를 언제 write 할 것인가
- damand - 필요할때
- precleaning - 미리 write

멀티프로그래밍의 정도
- 멀티프로세스의 개수를 조절함으로서 전체 메모리의 부하를 조절하는 방법이 있다.


#
Fetch policy
페이지를 세컨더리 스토리지로부터 메인메모리로 가져오는 방법은 크게 두가지
필요한것만 가져오기, 인접한것까지 같이 가져오기(hit rate 높임)


Demand paging
하나의 레퍼런스가 페이지에 대해 참조가 되었을때 메인메모리로 그 페이지를 가져옴
page fault 가 초반에 많이 생길수 있고, 초반 상태가 끝나면 급격히 줄어든다


Prepaging
많은 page fault가 동시에 생기는 demend paging의 단점을 줄이기 위한것
page fault가 난 페이지 말고 다른것도 한꺼번에 가져옴
세컨더리 스토리지에서 인접된 블록을 읽는것이 빠르기 때문에 효율적이다.
단점은 가져왔는데 소용이 없을경우 굉장히 비효율적이게 된다

swapping과 혼동하지 않도록 하자



#
Placement policy
가져왔을때 페이지를 real memory 어디에 놓을것인가, 세그멘테이션 방법에 있어서 중요한 요소. 
paging과 combined paging의 경우엔 중요한 일은 아님, 하드웨어 사용해서 사방에 흩어놓으면 되니까
근데 병렬처리기 중에서 NUMA에서는 중요한 역할을 하게 된다. 페이지를 놓는 위치에 따라서 접근속도가 다 달라서 




Replacement Policy
새로운 페이지가 메모리에 들어와야 될때, 기존의 메모리를 다 쓰고 있다면 어느 한 페이지 프레임을 비워서 새로 가져온 페이지를 넣어야 한다.
희생이 된 페이지가 다시 금방 들어와야 한다면 이것이 무의미한 동작이 된다.
미래에 쓰지 않을 페이지를 내보내야 한다.


Replacement하면 안되는 페이지의 경우 Frame Locking을 해서 Replacement하지 않도록 한다.
kernel의 control structures의 같은 경우(PCB), I/O버퍼, time-critical areas같은 경우는 Locking하게 된다
페이징의 경우에는 페이지 맵핑 테이블 안에 락을 나타내는 비트가 있어서 Locking가능


page replacement 알고리즘
최적, LRU, FIFO, Clock가 있다


가까운 미래에 사용되지 않을, 가장 먼 미래에 사용될 페이지를 선택한다
다른 방법의 성능을 확인하기 위해서 사용



가장 사용되지 않고 오래된 페이지를 제거
locality에 의하면 가장 최근에 사용되지 않은것은 미래에도 사용되지 않을 가능성이 높다.
문제는 페이지당 시간정보가 있어야하고 시간정보는 마지막 reference할때마다 업데이트 되어서 저장되어야 한다.
각 페이지마다 시간정보를 넣는것은, 이전에 페이지 테이블이 차지하는 양이 많다고 했는데 감당하기 어렵다.
이론적으로는 의미있는 방법 , 더 큰 크기의 블럭들을 관리할떄 사용된다



LRU는 시간 정보는 페이지 테이블 엔트리마다 유지해야해서 사용할수가 없었는데, 더 간단한 방법으로 round-robin style로 페이지를 제거한다. 가장 오래된것을 replace



각 페이지프레임에 대해 시간 정보 전체를 넣는 대신, 몇개의 추가적인 비트를 사용해서 비슷한 효과를 낸다
첫번째 로드가 될때, use bit를 1로 set 해서 사용되는것을 표시한다
페이지 프레임들을 원형 버퍼처럼 사용하면서
replacement 필요할때 use bit가 1이 되어있는것을 0으로 바꾸고, 0이 되어있는것을 새로운 페이지를 집어넣는다.

전체를 스캔할때마다 use bit을 지워서 파란색으로 표시,
2번을 5번으로, 5번은 *가 붙은 사용되는 상태에 있다.
그 다음에 파란색인 다음칸의 3을 제거하고 대체한다.

LRU보단 성능이 떨어지지만 FIFO보다는 성능이 좋은 방법이다
clock정보가 사용되는 대신 1비트의 use bit으로 비슷한 효과를 냈다. 여러개의 비트를 가지고 유닉스에서 쓰고 있다.


할당되는 페이지 프레임 개수가 늘어나면 차이가 아주 작아진다.
Clock이 거의 LRU와 유사하게 성능을 보이는것을 알수 있다.




#
페이징의 퍼포먼스를 좋게 하기 위해서, replacement를 할때 victim이 되는 페이지를 모아서 버퍼로 관리하는 방법에 관해서
replacement한 페이지를 디스크에 쓰는 일은 시간이 많이 들기에
시스템에서 버퍼를 관리해서 두개의 리스트를 가지고 관리하게 된다

하나는 modified page
하나는 write를 할 필요없는 page들의 list
replacement 되었을때 data같은 경우 modified 되지만
text, code의 경우에는 write할 필요가 없다, read only page이므로... 이것을 버퍼에 담고있다가 다시 필요하면 다시 가져오게 하고 그렇지 않으면 없애버리는 것으로 free page list를 사용
modified list는 수정된 페이지를 갖고있다가 디스크에 써준다 


페이지 버퍼링은 결국 캐시로 동작해서, page 버퍼 안에서 replacement를 어떻게 하느냐에 따라 성능에 많은 영향을 미친다. 그러나 대부분 OS는 선택을 임의로 하거나 FIFO로 하고 있다.


#
운영체제는 프로세스에 필요한 페이지 프레임의 개수를 결정한다
적게 이것을 만들수록, 많은 프로세스가 메모리에 들어올수 있다. 그러나 page fault 많아짐
그래서 전체 페이지를 몇개 할당할지 직접적으로 하는것이 좋다
그리고 working set이상으로 더 많이 할당하는 것은 page fault rate 줄이는데 도움이 되지 않는다


resident size를 할당하는 방법에는
고정개수를 할당하는 방법과 가변개수를 할당하는 방법이 있다
고정개수 할당시 page fault가 발생한다면, 자신이 갖고있는 페이지 프레임중 replaced될 페이지를 선택해야 한다.
가변개수라면 프로세스가 시작된 이후에도 할당된 페이지 프레임 개수를 바꿀 수 있다.



#
이러한 resident set을 유지하는 방법에 있어서 page replacement 대상을 global하게 할지, 아니면 프로세스 안에 한정되게 할지 두가지 방법이 있다.
local - 그 프로세스의 resident page 안에서
global - 메인메모리 전체에서 희생 페이지를 선택 


가능한 조합
fixed allocation의 경우에는 local replacement만 가능, 프로세스별로 고정된 크기의 페이지 프레임을 나눠주게 된다. 
replacement는 local하게 일어나서 그 프로세스에 할당된 페이지 프레임중 replace할 페이지 프레임을 선택하게 된다.

variable allocation은 프로세스가 실행되고 있는 중에 할당된 페이지 프레임 개수를 조절할수 있다는것
working set의 size변화에 맞춰서 프레임 개수를 조절가능하다.
이때 replacement를 local 하게 할수 있고, global 하게 할수도 있다.(각 프로세스별로 resident set, working set크기가 변할수 있으니까)


결국 통계에 의해 프로세스별로 필요한 페이지 프레임의 개수를 알아내고 할당할것인데, 너무 적게 할당하면 page fault가 많이 발생하고, 너무 크게 할당하면 메인메모리에 적은 수의 프로그램이 실행되니까 프로세스의 idle time이 커지고, 프로세스들이 swap하는 시간이 늘어나서 비효율적이게 된다.


variable allocation, global scope replacement하게 되면 
가장 구현하기 쉽고, os는 free frame의 리스트를 유지하면서
page fault가 생겼을때 resident set을 증가시키면서 free frame에서 빼서 할당하게 된다.
만약 frame이 더 이상 available하지 않으면 replacement하게 되고, 처리시간 줄이기 위해서 페이지 버퍼링을 사용하여 보완한다
많은 OS에서 사용


variable allocation, local scope replacement
새로운 프로세스가 메인메모리안에 들어오게 되면, 페이지프레임을 할당해주고, fault발생시 replacement할 페이지 선정하여 대치하고, 전체 성능을 확인하면서 프로세스에게 페이지를 할당하거나 회수하는 방법.

이때 할당하는 페이지 프레임 개수를 결정하는게 중요한 문제인데, 미래에 얼마나 페이지 프레임 개수를 사용하게 될까를 추측하는 문제이다.


중요한 그림은 아닌데 
할당되는 페이지 프레임의 개수가 커지면, 변화없이 유지되는 개수가 많아진다는것


use bit을 사용하고 있는데, 1이라면 이 페이지를 쓰고있다는것.
페이지 폴트 발생시, 일정시간 덜 사용된 페이지를 선택해서 대체하게 된다.
이 방법은 working set이 바뀌는 동안에는 잘 동작하지 않을 것이다.


working set을 estimate 하기 위해서 3개의 parameter사용
PFF가 locality가 변하는 동안에 잘 동작하지 않는다는것을 해결하기 위해서 사용

PFF방법이 고정된 구간에서 페이지가 사용되는 개수를 따져본다면
이것은 가변구간에 sampling interval 사용하여 page fault가 잘 일어나는지 sampling하는 방법. 구간을 조절하며 estimate



#
modified page는 세컨더리 스토리지에 반드시 쓰여야 하는데
쓸때 replacement가 일어날때만 쓰는것을 demand~ 이고
성능을 얻기 위해서 batches ,덩어리로 쓰는 법이 있다


이러한 방법 외에도
실행하는 프로세스의 개수를 조절하여 간접적으로 쓰래싱이 일어나지 않도록 하는 방법이 있다.
실행중인 프로세스의 개수를 멀티프로그래밍 레벨이라고 부르는데, 이것을 일정한 상태로 유지함으로서, 실행하는 프로그램 개수를 조절함으로서 안정적으로 프로그램이 돌아가게 할수 있다.
너무 적게돈다면 자원낭비
너무 많이돈다면 쓰래싱 발생
적절한 수를 유지한다


멀티프로그래밍을 하는 프로세스의 개수를 늘릴수록 성능이 올라가다가
80% 이후 성능이 떨어지는


이렇게 부하의 정도를 조절해서 쓰레싱이 발생하지 않도록 할때
중단하는 프로세스들을 없애는것이 아니고 swapped out시킬수 있다. (suspend)
어떤 프로세스를 suspend시킬 것인가
- 가장 낮은 우선순위
- fault를 발생시키는 프로세스
- 가장 늦게 activation 된것
- 가장 resident set이 작은, 되돌릴때 문제가 안생길 프로세스
- 커다란 프로세스
- 가장 많은 시간이 남은 프로세스를 중단시키는것




#
unix
paging system, kernel memory allocator 2가지 관리방법 제공


paging system은 
앞에서 설명한 가상기억장치를 제공하는 관리방법
kernel memory allocator는
가상기억장치가 사용되기 전에 커널 프로그램이 돌기위해서 메모리할당이 필요한데, 그때 사용


Age필드 - clock policy
valid - 메인메모리에 들어왔는지 나타내는
페이징을 하기위해서 이 자료구조를 사용한다



page replacement방법은 clock policy를 사용
가능한 페이지프레임 개수가 일정 이하로 떨어지면, 커널이 프레임들을 모으기 시작한다
어떤것을 victim으로 할지 결정하기 위해 clock policy 사용


kernel memory allocator는 
kernel이 실행될때 memory allocation받을때 사용


리눅스



페이지 테이블을 다계층 구조로 구성하여 크기를 줄이고 있다. 리눅스에서는 3개 계층구조


리눅스의 페이지 replacement는 clock 알고리즘 사용


kernel memory allocation은 커널에서 물리 메인메모리로 할당하는것을 제공
페이지 프레임 덩어리로 할당하고, 그것을 나눠서 메모리 할당할수 있도록 한다

작은 단위의 경우 inefficient
short term memory할당을 위해서 slab allocation 방법을 사용하고 있다



윈도우
버츄얼 메모리 매니저가 사용됨

32비트 프로세스 기반으로 만들어짐
32비트 주소 공간 갖는다. 총 4기가의 virtual address space를 프로세스당 갖는다. 윈도우에서 4기가 이상 메모리를 갖는 프로그램은 동작시킬수 없다.


이를 해결하기 위해서 64비트 윈도우즈 시스템이 사용됨
현재 사용되는 AMD64아키텍쳐는 32,64비트로 돌아감


4기가 바이트중 절반은 OS에서 사용되도록 맵핑되어있기에 32비트 플랫폼에서는 2기가바이트만을 사용자를 위해서 쓸수 있게 되어있다,


윈도우즈의 페이징
생성되었을때 프로세스는 2기가 메모리 사용가능
고정크기로 나누어져서 할당된다. 64K 바운더리로 나누어져 할당된다

3개 상태로 나누어져서 관리된다


윈도우즈는 resident set management system 가짐
variable allocation, local scope 사용
working set 은 프로그램이 실행되면서 메인메모리의 가용도에 따라 조절되고 관리된다.