Resident set은 전체 프로그램 중 현재 메모리에 올라와 있는 부분이다.
일부 페이지를 가지고 프로세스를 실행하다가 A Function이 필요한데, A Function이 들어있는 페이지나 세그먼트의 번호가 메모리에 없으면 인터럽트(Page fault)가 발생한다.
Interrupt에 해당하는 프로세스는 Block되고, OS는 다른 프로세스를 실행시킨다. Block된 프로세스의 I/O 작업이 끝나면 I/O 인터럽트가 발생하고 block됐던 프로세스는 Ready Queue로 이동한다.
Virtual Memory의 가장 큰 걱정거리는 Thrashing이다. 프로세스마다 사용가능한 공간에 제한이 존재한다.
Paging System으로 Virtual Memory를 구현했는데, 한 프로세스당 3 페이지만 메모리에 있을 수 있다면, 3 페이지를 놓고 실행을 하다가 메모리에 없는 페이지가 필요하다면 현재 메모리에 있는 페이지 중에서 하나를 버려야 한다.
결국 실행을 1, 2 줄 하다가 Swap out을 하게되고 페이지를 버리고 가져오는데 시간을 상당 부분 소요할 수 있다. 이런 상황을 Thrashing 이라 한다.
Thrashing 때문에 시스템의 속도가 저하되면 Virtual Memory System은 사용할 수 없을 것 같지만 Reference Locality 덕분에 페이지 변경 횟수가 줄어들게 된다.
Code와 Data는 연속된 공간에 위치하기 때문이다.
Page가 없으면 프로세스가 하드디스크로 쫓겨날까?
프로세스의 Swap Out이 아니라, 프로세스가 가지고 있는 한 페이지가 Swap Out되고 나머지 부분은 메모리에 존재한다.
프로그램을 실행하기 위해서는 2가지 요소가 중요하다.
Relocation은 Physical Address를 얻는 과정이다.
code는 변경되지 않고, data는 변경되는 경우가 많다.
modify bit가 1인 페이지의 경우, 버릴 때 데이터를 하드 디스크에 적고 버린다.
각각의 프로세스마다 Page Table을 가지고 있다.

Page Table entry는 메인 메모리안의 해당하는 페이지의 frame#가 있다.

하지만 이러한 경우는 운이 좋다고 할 수 있다.
페이지 테이블은 프로그램 크기가 커지면 크기가 상당히 커져서 한 페이지 안에 들어가지 못할 수도 있다. 위와 같은 상황은 페이지 테이블이 한 페이지보다 작은 경우이기에 가능하다.
만약 페이지 테이블이 한 페이지가 넘어가게 되면, 페이지 테이블 중 누구는 메모리에 있고 누구는 메모리에 있지 못하게 된다. 페이지 테이블의 각 페이지가 메모리에 있는지 없는지, 있다면 어떤 프레임에 있는지 알 수 있는 페이지 테이블이 필요하게 된다.

전체 프로그램 중에서 어떤 코드를 실행하고 있다. 그 코드에 대한 페이지 정보가 2번 페이지에 있다면, 2번 페이지만 메모리에 집어넣으면 된다.
페이지가 메모리에 있는지 없는지, 어디에 있는지도 알아야 한다. 그래서 결국 페이지 테이블의 페이지 개수가 상당히 많기 때문에 페이지 테이블의 각 페이지가 메모리에 있는지 없는지, 있다면 어느 프레임에 있는지 알려주는 페이지 테이블이 필요하다.

이 페이지 테이블은 페이지 테이블이 어디에 있는지 알려주는 테이블이다. 이 페이지 테이블의 크기는 = 4K 인데, 페이지 하나의 크기와 같다. 이 페이지를 root page table 이라 한다.
root page table 에는 페이지 테이블에 대한 정보가 있다. 각각의 페이지 테이블에 가면 실제 프로그램의 각 페이지에 대한 정보가 있다.
위 계산은 프로그램의 크기와 페이지의 크기가 가정한 계산이다. 만약 프로그램 크기가 더 크거나 페이지가 더 작다면, 페이지 개수가 더 많아진다. 페이지 개수가 많아지면 페이지 테이블의 크기가 커진다. 그러면 페이지 테이블을 포함하는 페이지의 개수도 많아지고, 루트 페이지 테이블이 한 페이지 안에 안들어온다.
따라서 루트 페이지 테이블 - 1단계 페이지 테이블 - 2단계 페이지 테이블 같은 식으로 전개가 된다.
위 이미지는 계산이 맞아 떨어지기 때문에 루트 페이지 테이블은 4K, 다음 단계 페이지 테이블의 크기는 4M, 프로그램의 크기는 4G이다.
프로그램이 더 크거나 페이지의 크기가 작으면 단계가 하나 더 늘어날 수 있다.

해당 부분이 내가 원하는 페이지 테이블의 시작주소이다. 그 후 Virtual Address의 두번째 페이지 테이블에서 몇번째 entry인지 번호를 더하면 그 페이지 테이블 안에서 해당하는 데이터가 들어있는 프레임 정보의 엔트리의 주소가 계산이 된다. 그 프레임 정보를 가져다가 오프셋을 붙이면 내가 원하는 데이터의 주소가 된다.
데이터를 읽으러갈 때 루트 페이지 테이블을 읽고, 2단계 페이지 테이블을 읽고, 그 다음 데이터를 읽는다.
페이지 테이블의 크기가 상당히 크다. 프로그램에 대한 페이지 테이블의 크기가 커지고, 여러 단계의 페이지 테이블을 가지게 된다.
4G의 프로그램은 개의 페이지 개수를 가진다. 그럼 전체 페이지 테이블의 엔트리가 개이다.
1M개의 엔트리가 있다. 그런데 동시에 실행시킬 수 있는 프로그램이 100개라고 한다면, 100 * 개의 페이지 테이블 엔트리가 필요하게 된다.
그런데 만약에 메모리 상에 동시에 여러 프로그램이 들어갈 수 있고 이 시스템이 한 프로세스 당 페이지 3개씩 메모리에 넣고 시작한다고 하자. 페이지 3개만 메모리에 들어있을 것이다. 개의 엔트리 중에서 3개만 presence bit가 1이다. 이는 상당한 공간 낭비이다. 어차피 메모리에 없기 때문이다. presence bit가 1인 메모리에 있는 페이지만 올려놓으면 페이지 테이블의 크기가 상당히 줄어든다.
위의 상황에서 메모리에 있는 페이지 엔트리만 골라서 만든 페이지 테이블이다.
Page table의 k번째 entry에는 k번째 page frame에 저장된 page에 대한 정보가 저장
각각의 페이지가 어디에 있는지를 찾아가는게 아니라, 메모리에 있는 페이지 테이블이 무엇인지 시작한다.
Inverted Page Table을 사용하게 되면, 실제 시스템안에 프로세스가 몇개가 동시에 실행되는지, 한 프로세스의 크기가 얼만큼이 되어서 페이지 테이블의 크기가 얼만큼이 필요한지가 전혀 중요하지 않다. 메모리에 있는것만 기록하기 때문이다. 실제 real 메모리 안에 프레임이 100개가 있다면 페이지 테이블의 엔트리는 100개이다. 만약 프레임 개수가 400이면 페이지 테이블 엔트리는 400개이다.
이와같은 페이지 테이블을 만드려고 할 때 어떤 정보를 넣어야 할까?
Page number와 Process identifer가 페이지 내용으로 들어가고 인덱스로 프레임 번호가 사용한다.

4번 엔트리안에는 페이지 프레임 4번에 들어있는 프로세스는 어떤 프로세스의 어떤 페이지다 하는 정보가 들어있다. 그런데 문제는 원하는 페이지가 어떤 프레임에 들어있는지 알아보려면 어떻게 찾아갈까?

예를 들어, 2350번 프로세스의 33번 페이지에 접근하려고 한다. 문제는 인덱스만 보고는 알 수 없다. 하나씩 살펴봐야 하는데 이는 결국 다 읽어봐야 한다. 공간적인 면에서는 상당히 save했지만, 시간적인 면에서 보면 다 찾아봐야 한다. 따라서 빨리 찾아가는 방법이 필요하다.
Chain pointer는 같은 hash function의 결과를 갖는 엔트리들 끼리 링크로 연결한다. 링크의 다음번 원소에 위치를 적어놓는다.
만약에 hash function으로 %10을 사용한다고 하자. 메모리에 들어있는 모든 페이지의 번호 % 10 이 같은 페이지들끼리 링크로 연결한다.
위의 페이지 번호 % 10 하면 3으로 같다. hash function을 적용시켰을 때 %10 한 나머지가 3인 페이지들의 첫번째 출발점은 인덱스 0이다. 그 다음은 인덱스 3이다. 오른쪽 끝의 값이 다음 인덱스를 가리키고 있다.
만약 2350번 프로세스의 33번 페이지를 찾는다면, 모두 탐색하는 것이 아니라 33번 이라는 페이지 번호를 hash function의 입력으로 준다. 그러면 출력값은 hash functon의 결과값인 3을 출력하는게 아니다. 나머지가 3인 페이지들이 전부 저장된 장소가 링크로 연결되어 있다. 이 링크의 첫번째 출발점을 출력한다.
여기서는 0번 엔트리를 찾아간다. 링크를 따라 내려가다 12번에 가면 원하는 엔트리이다. 프레임 번호는 사실 이전의 9번의 chain 안에 들어있다. 따라서 이 값을 프레임 번호에 집어 넣는다.
공간이 상당히 많이 소요된다.
공간이 최소화되지만 찾으러가는데 링크를 따라가야 하기 때문에 시간이 걸린다.
어떤 방법을 사용하던지 간에 최소 2개 이상은 필요하다.
여러 번 메모리에 접근하는 것은 CPU에 비해 상당히 느리다. 그래서 캐시를 사용하는 것처럼 페이지 테이블을 위한 캐시를 사용한다. 이를 Translation Lookaside Buffer 라고 한다. 페이지 테이블 엔트리 중에서 자주 사용하는 엔트리를 TLB에 넣어둔다.
TLB에 저장된 정보 : 실행중인 process가 access하려고 하는 명령 또는 데이터가 들어있는 page가 적재된 page frame 번호를 포함하는 page table entry
TLB안에는 몇 번 페이지가 몇 번 프레임에 있고 관련된 정보가 어떤지 들어있다. 그 다음부터는 메모리에 가지 않고 TLB에서 찾으면 된다. 그런데 여기서 몇 번에 한 번씩 메모리에 가야할까?
페이지 크기가 4K이고 명령어 하나가 4 bytes라고 할 때, 1024개의 명령 또는 데이터가 들어있다. 이 페이지의 A라는 함수를 시작한다고 한다면, 함수가 잘 짜여진 경우 이 페이지 안 1000개의 명령을 실행할 동안 다른 페이지에 갈 일이 없다. 그러다 1000번에 한 번 페이지가 바뀌게 되는데 그 때 TLB를 다시 세팅하면 된다. TLB를 사용하면 훨씬 효과적으로 페이지 테이블 서치가 가능하다.

Virtual Address의 페이지 번호가 주어지면 TLB를 찾아본다. TLB에 원하는 정보가 있으면(TLB hit) 프레임 번호를 붙인다. 만약 TLB에 없으면(TLB miss) 페이지 테이블을 찾는다. 찾은 정보로 주소를 만들고, 테이블을 TLB에 옮겨놓는다. 페이지 테이블까지 가봤는데 없으면(page fault) 하드 디스크까지 가서 원하는 페이지를 가지고 와야 한다.
대부분 같이 사용하는 코드가 한 곳에 모여있기 때문에(Locality) 자주 하드 디스크에 가지는 않는다.

일반적인 페이지 테이블의 경우 인덱스로 찾아간다. 그런데 TLB는 associative mapping을 사용하는데 찾으려고 하는 데이터가 아예 원소안에 들어있다. 그래서 한꺼번에 내용을 비교한다.
페이지 크기가 굉장히 작으면, 똑같은 프로그램도 더 잘게 나눠진다. 페이지 수가 많아지게 되면 페이지 테이블이 커지게 된다. 페이지 테이블이 커질수록 더 많은 공간을 차지하게 된다. 따라서 페이지의 크기는 너무 작으면 안된다.
그런데 페이지의 크기가 작으면 작을수록 page fault가 발생할 가능성이 작다. 페이지 크기가 크면 page fault가 발생할 가능성이 크다. 페이지 크기가 작으면 내가 원하는 페이지가 메모리에 있을 확률이 크다.
만약 한 프로세스한테 각 프로세스마다 동일한 크기의 공간을 할당한다고 하자. 이 공간에는 2개의 페이지가 들어갈 수 있는 이 프로세스는 3개의 페이지의 코드 부분을 실행해야 한다. 그런데 2개밖에 넣을 수 없다. 그러면 계속해서 page fault가 발생한다.
그런데 반대로 메모리 공간은 똑같이 할당이 됐지만 프로그램이 페이지 크기를 잘게 나눴다고 해보자. 페이지 크기가 작아졌기 때문에 각 부분이 들어있는 페이지가 공간에 들어올 수 있게 된다. 원하는 부분들만 골라서 넣을 수 있게 된다. 따라서 페이지 크기가 작을수록 원하는 부분들만 메모리에 집어넣을 수 있다. 그래서 page fault가 발생하지 않는다.
위의 두 가지 결론이 상반된다. 페이지 크기는 너무 작아도 안되고 너무 커도 안된다. 최적의 페이지 크기를 구해낼 수가 없다. 그래서 실제 시스템마다 페이지 크기가 다르다.
어떠한 코드를 의미있는 단위로 나눈다. 관련된 함수들을 묶어서 하나의 segment로 만들거나, 관련된 데이터가 저장된 곳을 segment로 만드는 것처럼 의미있는 단위로 만든다.

세그먼트 테이블에 length와 base가 포함이 되어야 한다. length는 세그먼트의 길이이고 base는 세그먼트의 시작주소이다.
이외에도 세그먼트가 메모리에 있는지 없는지를 나타내는 presence bit가 필요하다.
세그먼트가 메모리에 올라와서 업데이트 됐는지를 나타내는 modify bit가 필요하다.

그러나 segmentation으로 virtual memory를 구현하는 것은 굉장히 비효율적이다. 그래서 실제로는 segmentation 기법으로 virtual memory 시스템을 구현하지 않는다.
프로그램을 의미있는 단위로 나눈다. 그 다음 각각의 세그먼트를 다시 페이지로 나눈다.
하나의 세그먼트가 몇 개의 페이지로 나뉘고 실제 메모리 안에는 페이지 프레임안에 페이지가 들어간다. 따라서 external fragmentation이 없다.
하지만 세그먼트로 먼저 나눴기 때문에 코드 부분의 마지막 페이지도 코드만 들어있다. 어떤 페이지도 코드와 데이터 영역에 걸쳐져 있지 않는다. 따라서 sharing과 protection이 쉽다.
만약 어떤 페이지가 반은 코드, 반은 데이터라면 share/protection을 해야할지 말아야할지 정할 수 없다.

세그먼트 테이블에서 length는 무엇을 나타낼까?
세그먼트로 나눈다음 각각의 세그먼트를 페이지로 나눈다. 실제로 메모리에 들어갈 때는 페이지 단위로 들어간다. 그러면 메모리에 들어가있는 한 페이지들은 길이가 일정하다. 여기서 세그먼트 base는 뭘 나타낼까?
length : 세그먼트에 속하는 마지막 페이지의 번호
base : 세그먼트에 속하는 페이지 테이블의 시작 주소

어떻게 주소변환 되는지를 보면 앞의 두가지가 어떤 의미인지 알 수 있다.
Virtual Address는 세그먼트 번호, 페이지 번호, 오프셋이 있다.
메인 메모리에 데이터에 접근하기 위해서는 오프셋은 그대로 가져다쓰고 프레임 번호만 붙여지면 된다.
페이지 테이블안에 내가 원하는 프레임 번호가 들어있다. 이 페이지 테이블안에서 몇 번째 엔트리인지는 Virtual Address의 페이지 번호에 들어있다. 이 페이지 테이블은 세그먼트마다 있다.
세그먼트의 크기는 동일하지 않다. 따라서 페이지 테이블마다 페이지 테이블 크기도 제각각이다. 따라서 기본적으로 페이지 테이블도 당연히 메모리에 들어가지만 어디에 들어가는지 알 수 없다.
그래서 세그먼트 테이블의 base값은 내가 원하는 주소나 데이터가 들어있는 페이지의 시작주소가 아니라, 페이지에 관한 정보를 담고 있는 페이지 테이블의 시작주소이다.
base에다가 페이지 번호를 더하게 되면 해당하는 페이지 테이블의 k 번째 엔트리 값을 알 수 있다. 거기에 가면 해당하는 프레임 번호를 찾을 수 있다.
length 값은 페이지의 길이가 아니다. 페이지의 개수이다.
만약 virtual adress에서 세그먼트 번호는 3이고, 페이지 번호는 15라고 해보자. 3번 세그먼트를 보니 length값이 5라면, 15번 페이지는 존재하지 않는다.
레지스터 안에는 세그먼트 테이블의 시작주소를 가지고 있다. 이 값에다가 세그먼트 번호를 더해서 원하는 엔트리를 찾아간다.
페이지 시스템을 사용할 때 고려사항이 있다. 어떤 페이지를 가져와야 하는 상황에서 해당 페이지만 가져올 것인지, 아니면 해당 페이지 다음에 있는 페이지도 같이 가져올 것인지 고민해야 한다.
하드디스크에서 무언가를 가져오는 데에는 시간이 굉장히 오래 걸린다. 그런데 가서 얼만큼 가져오는지는 상관이 없다.
Fetch= 하드디스크에 가서 내가 원하는 페이지를 가져오는 것
- Demand paging
- 내가 필요할 때 필요한 페이지만 가져오는 것
- Pre-paging
- 필요한 페이지 다음 페이지까지 같이 가져온다.
- 별일이 없으면 프로그램을 sequencial하게 사용하기 때문에 다음 페이지 내용을 미리 가져다 놓는다.
Demand paging 이 더 좋다.
Virtual Memory를 사용하는 이유는 공간이 부족하기 때문이다. 공간이 부족한데 당장 필요한 페이지가 아니라 필요한 페이지를 미리 가져다 놓는 것은 공간을 낭비하게 된다. 따라서 전체 공간의 일부분은 지금 사용하지 않는 페이지로 채워지게 되는 상황이다.
새로운 페이지를 가져오려면 결국 한 페이지를 버려야 한다. 이때, 어떤 페이지를 버릴 것인지 정해야 한다.
한 페이지를 버릴 때 가장 좋은 방법은 버린 페이지를 다시 사용하지 않는 것이다. 또는 다시 사용하게 되더라도 아주 오랫동안 사용하지 않으면 좋다. 그래서 가장 오랫동안 사용하지 않을 페이지를 버리는 것이 좋은데, 문제는 이 것을 알 수가 없다. 그래서 시스템들은 가장 오랫동안 사용하지 않을 페이지들을 예측해야 한다. 이때 Reference Locality를 이용하여 예측한다.

페이지 번호는 1~5번이 존재하고 프로세스한테 할당된 page frame은 3개밖에 없는 상황이다.
page fault가 발생하면 하드디스크에서 페이지를 가져올 때까지 멈춰있어야 하므로 Block 상태가 된다. 그래서 page fault가 많이 발생한다는 말 자체가 프로그램 실행이 느려진다는 뜻이다.
이 때 여러가지 방법들을 사용했을 때 page fault가 몇 번 발생하는지 확인해보자.

5번 페이지가 필요한 시점에서 어떤 페이지를 버릴지 결정해야 한다. Optimal Policy는 앞으로의 사용을 다 알고있기 때문에 앞으로 사용되지 않는 1번 페이지를 버린다. 이때 page fault가 발생한다.
그 다음 4번 페이지를 가져오기 위해서 버릴 페이지를 찾는다. 4-5-3-2 순서로 사용되는데 2번을 제일 나중에 사용하기 때문에 2번 페이지를 버리고 4번 페이지를 가져온다. 이때 page fault가 발생한다.
그 다음 2번 페이지가 필요할 때 3, 4 번 페이지를 버려도 된다. 이처럼 여러 개를 버릴 수 있는 상황에서 결국 for 문을 사용하기 때문에 첫번째 위치에 있는 4번 페이지를 버린다. 이때 page fault가 발생한다.
가장 적게 사용된 페이지를 찾아야 하기 때문에 모든 페이지를 다 조사해야 한다. 실제 시스템에 적용하려면 시스템 안의 page frame 숫자만큼 비교해야 한다.
이 때 두 가지 문제점이 있다.

5번 페이지를 교체하는 시점에 1, 2, 3 중 제일 오랫동안 사용되지 않은 3번 페이지를 버리고 5번 페이지를 넣는다. 이때 page fault가 발생한다.
4번 페이지로 교체하는 시점에 가장 오랫동안 사용되지 않은 1번 페이지를 버리고 4번 페이지가 들어간다. 이때 page fault가 발생한다.
3번 페이지가 필요한 시점에 가장 오랫동안 사용되지 않은 2번 페이지를 버리고 3번 페이지가 들어간다. 이때 page fault가 발생한다.
2번 페이지로 교체하는 시점에 가장 오랫동안 사용되지 않은 4번 페이지를 버리고 2번 페이지가 들어간다. 이때 page fault가 발생한다.

5번 페이지로 교체하는 시점에 첫 번째 프레임에 있는 2번 페이지를 버리고 5번 페이지를 넣는다.
바꾸자마자 2번 페이지가 필요해서 두 번째 프레임에 있는 3번 페이지를 버리고 2번 페이지를 넣는다.
4번 페이지가 필요해서 세 번째 프레임에 있는 1번 페이지를 버리고 4번 페이지를 넣는다.
이처럼 진행하면 6번의 page fault가 발생한다.
LRU를 구현하는 데에 시간적, 공간적 오버헤드가 크고 FIFO는 시간적, 공간적 오버헤드는 거의 없지만 page fault 횟수가 너무 많다.
따라서 적절한 중간 지점인 Clock Policy 존재

현재 시계바늘이 2번 프레임을 가리키고 있는데, 페이지를 교체해야 하는 상황이 왔다. 교체할 페이지를 찾으러 가야하는데 2번은 use 비트가 1이다. 그래서 2번의 use 비트를 0으로 만들고 지나간다. 3번 또한 use 비트를 0으로 만들고 지나간다.
그 다음 4번 프레임에 있는 페이지의 use 비트는 0이다. 그래서 4번 프레임에 있는 페이지를 교체한다.
Clock Policy는 LRU의 오버헤드를 둘 다 해결했다고 볼 수 있다.
Example

5번 페이지로 교체하는 시점하려고 할 때 바늘은 2번 페이지를 가리키고 있다. 이때 시계 바늘이 움직이면서 use 비트를 0으로 만들고 움직인다. 그러다 한 바퀴를 다 돈 다음 2번 페이지의 use 비트가 0이므로 2번 페이지를 교체한다.
이 상황에서 2번 페이지가 필요한데 바늘이 가리키고 있는 위치의 페이지의 use 비트는 0이다. 따라서 바로 교체한다. 이와 같은 방법으로 페이지를 교체하게 된다.
그런데 페이지 프레임에 이미 있는 페이지가 필요할 때는 어떻게 될까? 이 때는 page fault가 발생한 것이 아니라, 해당 페이지의 use 비트만 1로 바꿔준다.
바늘이 움직이는 것은 페이지가 메모리에 없어서 새로운 페이지를 가져와야 하는데, 어디서 가져올지를 찾기 위해 움직인다. 그래서 지금처럼 해당 페이지가 메모리에 있는 상황에서는 바늘이 움직이지 않는다.
Optimal에 가장 가까운 방법이 LRU이고, 가장 안좋은 방법은 FIFO이다.

Clock 기법은 프레임 숫자가 10개가 되기 전까지는 LRU와 FIFO의 중간 정도이지만, 10개가 넘어가면 거의 LRU와 비슷하게 좋다.
각각의 프로세스마다 프레임을 할당받는 사이즈가 다르다.
Resident Set = 메모리안에 들어있는 페이지 집합
사이즈가 고정될 수도, 변동할 수도 있다.

Working SetResident SetFixed Allocation 과 Variable Allocation 중 Variable Allocation 방법이 더 효과적이다.
그런데 어떤 페이지가 필요하면 기존에 있던 페이지 중에 하나를 버려야 한다. 이때 자신한테 할당된 페이지 중에서 하나를 버릴지, 다른 프로세스의 페이지를 버릴지 결정해야 한다.
Fixed Allocation은 자기한테 주어진 공간이 정해져 있으므로 local scope 밖에 사용할 수 없다.
Variable Allocation은 Local Scope, Global Scope 다 가능하다.
Clock 기법 사용한다.
메모리 안에 있는 모든 페이지를 보면서 use bit가 0인지 확인한다. use bit가 0이라는 뜻은 사용하지 않는 페이지다. Working Set이 큰 프로세스는 점점 더 많은 프레임을 할당받게 된다. 반면 Working Set이 작은 프로세스는 점점 더 작아진다.
물론, 시스템안에 있는 모든 프로세스가 Working Set이 커지는 구간을 지난다면 Page Fault가 자주 발생할 수 있다. 그러나 그렇지 않은 경우에는 저절로 Working Set이 맞춰진다.
local scope는 자신의 페이지를 버리기 때문에 프레임 개수가 일정하다. 하지만 Variable Allocation이므로 Working Set에 맞춰서 계속 변화시킨다.
Page Fault가 얼마나 자주 발생하냐에 따라 페이지 프레임 수를 조정한다.
Allocation을 정하고, global, local을 정한다.
두 가지 방식을 같이 사용한다.
커널과 유저 프로세스의 메모리 할당 방식이 다르다.
메모리 교체 시 Clock 기법 사용 ⇒ Variable Allocation-Global Scope
But, 변형된 Two-handed 기법을 사용

유닉스에서는 페이지를 교체하는 시점에서 바늘이 움직이는 것이 아니라 평상시에 OS가 할 일이 없을 때 바늘을 움직인다. 그래서 reference bit가 0인 페이지를 미리 찾아서 Paged-out 리스트를 만든다.
리스트를 만들어놓으면 두 가지 장점이 있다.
그런데 가끔 Paged-out 리스트에서 페이지가 사용이 되는 경우가 있다. 그럴 때는 reference bit를 1로 만들고 다시 사용한다.
Paged-out이 길어진다 ⇒ 두 바늘이 가깝게 붙어있다.
두 바늘의 속도를 OS가 조절한다.
Variable allocation - Local Scope 사용 ⇒ LRU
모든 프로세스들은 실행을 시작할 때 Fixed된 공간을 할당받는다.
page fault가 많이 일어나면 프레임을 늘려주고 적게 일어나면 프레임을 줄여준다.
Working Set이 전부 다 Resident Set이 되게 하려고 한다.
Working-set based memory management
LRU-Local Scope ⇒ 시간, 공간적으로 소모가 적다.