DRAM의 크기가 제한되어 있기 때문에, 동시에 실행할 수 있는 프로세스의 개수가 제한되어 있다.
동시에 실행 가능한 프로세스의 개수를 늘리기 위해 Virtual Memory를 도입하게 된다.
Memory Use Pattern
프로그램이 실행될 때, 많은 부분이 접근되지 않거나 실행되지 않는다.
1. 일부분의 코드만 실행
void main() { ... if (i == 0) a(); else if (i == 1) b(); else c(); ... exception(e == 0) handle_error(); }
위의 코드에서
if
문은 3개의 분기 중 오직 하나만 선택되게 된다.
또한exception
에서e != 0
일 때에는,handle_error()
가 실행되지 않는다.2. 제한된 메모리 접근
int a[1000][1000];
에서, 만약0 <= i, j <= 100
에 해당하는
데이터만 사용하게 된다면, 나머지 공간들은 전혀 사용되지 않는다.
위의 경우에서 프로그램의 일부분만이 사용되기 때문에,
사용되지 않는 부분을 굳이 메모리에 올려 사용할 필요가 없다.
이를 해결하기 위해 Demand Paging기법이 도입되었다.
위에서 설명한 바와 같이, 실행에 필요한 코드 / 데이터만을 메모리에 올려 실행하는 것을 Demand Paging이라 한다.
Demand Paging의 장점은 다음과 같다.
Executable & Virtual Memory
Executable (실행 파일)
실행 파일은 여러 Page로 나누어 Disk에서 관리되고 있다.
Virtual Address Space
Virtual Address Space는 프로그램 실행 시 할당된다.
Executable - Virtual Memory
프로그램이 실행될 때, Disk의 Executable과 Virtual Address Space가 Mapping된다.
이 때 Mapping만 해주고 Memory(DRAM)에 적재하진 않는다.
Mapping 정보는 Virtual Address Space의 공간에 저장해놓는다.
Virtual Memory에 있는 Page를 접근하려고 해당 Page Table Entry를 참조할 때,
해당하는 Page가 Physical Memory에 없는(Valid-Invalid Bit = i
) 현상을 Page Fault라 한다.
이 때 Page Fault Exception이 발생하게 된다.
Page Fault Exception
- 1. CPU가 Page Fault Exception 이벤트를 발생시킨다.
- 2. Kernel에 Exception을 전달한다.
- 3. Kernel은 Page Fault Handler을 실행시킨다.
- 4. Virtual Memory에 정의되어 있는 함수를 실행시킨다.
- 5. 호출된 함수에서는 먼저 Page Fault의 원인을 분석하여,
- (1) Disk에서 코드 또는 데이터를 불러온다.
- (2) 프로세스를 종료한다.
5-(1)의 경우 다음 작업을 실행한다.
- 1. 현재 실행되고 있는 프로세스를 일시정지한다.
- 2. Exception을 발생시킨다.
- 3. OS는 이를 받아 Disk에서 해당 페이지를 검색한다.
- 4. 찾은 페이지를 DRAM의 빈 프레임에 적재한다.
- 5. 적재된 프레임과 Page를 비교하여 Page Table에 등록하고,
valid
상태로 변경한다.- 6. 일시정지된 프로세스를 시작시킨다.
5-(2)는 다음과 같은 경우에 발생한다. (예시)
- 1. 코드상에서 OS가 할당된 주소를 참조한다.
- 2. 값을 변경하려고 시도할 때, Page Table의 상태가
invalid
이다.- 3. Page Fault Exception을 발생시켜 Kernel에 넘긴다.
- 4. Kernel은 Page Fault Handler를 실행하여 원인을 분석한다.
- 5. 이 때 발생한 Exception은 DRAM상에 할당할 수 없는 주소에 접근한 것이기 때문에
SIGSEGV
시그널을 프로세스에게 보낸다.- 6. 프로세스는 이 시그널을 받으면
화면에 Segmentation Fault 메시지를 띄운 후 프로세스를 강제 종료한다.
p
: Page Fault Rate, ma
: Memory Access Time이라 할 때,
Effective Memory Access Time = Page Fault Time
이 때 Page Fault Time은 다음과 같다.
위 3개의 시간이 Page Fault Time을 구성한다.
역시 디스크에서 불러올 때가 시간이 제일 오래 걸린다.
메모리가 모두 사용되고 있는 경우, Page Fault가 발생하여 디스크에서 새로운 데이터를
DRAM에 적재해야 하는 경우, 다음과 같은 방법을 사용할 수 있다.
Process Termination
Page Fault가 발생하면 프로세스를 종료시킨다.
이는 새로운 데이터를 적재하지 못하므로 사용되지 않는다.
Process Swaping
다른 프로세스가 사용중인 Page를 모두 Disk에 내보내고 (Swap out)
나중에 해당 프로세스가 실행될 때 다시 Page를 불러오는 방식 (Swap in)
프로세스가 페이지를 많이 사용중인 경우 I/O 시간이 오래 걸리므로 거의 사용되지 않는다.
메모리가 극도로 부족할 때 최후의 방법으로 사용
Page Replacement <<<
Page Fault가 발생하면, Physical Memory에서 교체할 페이지를 찾는다. (Victim Page)
Victim을 Disk에 백업하고, Victim에 해당하는 Page Table Entry의 상태를 invalid
로 전환한다. (Page out)
그 후 불러올 데이터를 Disk에서 Physical Memory에 적재하고, 상태를 valid
로 전환한다. (Page in)
Swap Space
Page out시에 Disk에 해당 페이지를 저장하는 공간이 존재한다.
이 공간을 Swap space라 하고,
리눅스에서는 보통 DRAM size * 2로 설정한다.
Modify Bit (in Page Table)
Disk에서 불러와 DRAM에 적재한 데이터가 한 번도 값이 쓰여지지 않았을 때,
이를 Clean bit로 표현한다.반대로 Disk에서 불러온 데이터의 값이 DRAM상에서 변경되었을 때,
이를 Dirty bit로 표현한다.Clean일 때는 Page Replacement 시에 Disk에 백업을 하지 않아도 되지만,
Dirty일 때는 DRAM에 있는 데이터가 최신 데이터이므로 Disk에 백업해야 한다.
Page Fault가 발생했을 때, 어떤 프레임을 선택하여 교체할 지(victim)
결정하는 방법을 Page Replacement Algorithm이라 한다.
이 때 Page Fault가 가장 적게 발생하도록 프레임을 결정하는 것이
좋은 알고리즘의 조건이다.
reference string
: 일련의 메모리 접근 순서
메모리에서 가장 오랫동안 머물렀던 프레임을 victim으로 설정하는 방법
reference string
에서 가장 먼 미래에 사용될 프레임을 victim으로 설정하는 방법
현실적으로 reference string
은 아무도 알 수 없으므로 사용할 수 없다.
Lease Recently Used의 줄임말로, Optimal 알고리즘을 흉내낸 것이다.
가까운 과거에 접근된 페이지는 가까운 미래에 접근될 가능성이 높고,
먼 과거에 접근된 페이지는 먼 미래에 접근될 가능성이 높다는 이론에 기초
완벽하게 Optimal 알고리즘을 대체할 순 없지만,
가장 효율적인 방법으로 현재 많이 사용되는 방법이다.
Page Table에 reference bit
를 추가시켜,
MMU에서 해당 페이지를 참조하면 bit를 1로 전환시킨다.
하드웨어적으로 비트만 바꿔주기 때문에, 커널은 어떠한 순서대로
페이지가 접근되었는지 알 수 없다.
Page-out Daemon
Page out Daemon은 커널에서 주기적으로 실행된다.
메모리 부족이 매우 심하면 페이지를 자주 교체해야 하기 때문에,
더 자주 실행된다.이 Daemon이 실행되는 주기 사이에 페이지가 접근되면,
가까운 과거에 사용되었다고 판단하여 가까운 미래에 접근될 가능성이 있다고 판단한다.
커널에서는 Page들을 circular linked list
로 관리하는데,
Daemon이 실행되면 이 리스트를 head
부터 개 탐색하여
해당 페이지의 reference bit
를 검사한다.
이 검사 과정에서 reference bit
가 0일 경우
가까운 과거에 접근되지 않았다고 판단하여,
이 페이지를 victim으로 설정해 교체해버린다.
reference bit
가 1일 경우
가까운 미래에 접근되었다고 판단하여, 한 번의 기회를 더 주게 된다.
이 기회라는 것은 해당 페이지의 reference bit
를 0으로 만들고,
메모리 상에서 놔두는 것을 의미한다.
개만큼 탐색 후 Daemon이 종료되고 다음에 다시 Daemon이 실행되면
이 실행 시에는 번 부터 다시 리스트를 탐색하여 동일한 작업을 수행한다.
이 방법이 실제 상용 OS에 탑재되어 있다.
Page table에 counter
가 4바이트 정도 들어가게 된다.
페이지가 접근될 때 마다 counter
가 1씩 증가한다.
페이지 교체를 할 시점에,
counter
가 가장 적은 페이지를 교체하는 방법을 LFU(Least Frequently Used)라고 한다.
반대로, counter
가 가장 많은 페이지를 교체하는 방법을 MFU(Most Frequently Used)라고 한다.
이 방법은 Table에 4바이트가 추가되었기 때문에, 실제로 사용되지는 않는다.
짧은 시간에 Page Fault가 급격히 많이 일어나는 현상을 Thrasing이라 한다.
위 그림처럼 메모리 용량이 적을 경우, 페이지 교체 시 Page Fault가 급격히 일어나게 된다.
프로세스 2의 페이지로 메모리가 꽉 찼을 때 프로세스 1이 실행되면,
Page Fault가 엄청나게 일어나게 되고, 이 과정이 반복되게 된다.
유일한 해결 방법은 메모리를 큰 걸로 교체하는 것이다.