가상 메모리 시스템(Virtual Memmory System)과 페이지 폴트(Page Fault)
가상메모리(Virtual Memory)란?
가상 메모리는 메모리를 관리하는 방법의 하나로,
각 프로그램에 실제 메모리 주소가 아닌 가상의 메모리 주소를 주는 방식을 말한다.
이러한 방식은 멀티태스킹 운영 체제에 흔히 사용되며, 실제 주기억장치(RAM)보다 큰 메모리 영역을 제공하는 방법으로 사용된다.
가상 메모리 개념의 배경
"RAM은 결코 충분하지 않다"
가상 메모리의 개념을 사용하지 않는 시스템에서 어플리케이션 수행 시 10000바이트의 용량을 차지하
는 프로그램이 있다고 가정해보면, 시스템의 메모리는 최소 10000바이트 이상이 되어야 할 것이다.
만일, 1바이트라도 모자란 9999바이트 용량의 RAM으로는 "메모리부족" 오류가 날 것이다.
즉, 한 시스템에서 돌아가는 가장 큰 실행 어플리케이션 용량보다 메모리가 더 커야한다는 것이다.
여기에 두 개이상의 프로그램이 멀티태스킹 된다면 상황은 더 안 좋아질 것이다.
가상 메모리는 프로그램이 차지 하는 주소공간(Address Space)의 개념에 대해 조금 다르게 접근한다.
가상 메모리를 사용하는 운영체제는
"프로그램을 실행하는데 얼마나 많은 메모리가 필요한가?"에 집중하지 않고,
"프로그램을 실행하는데 필요한 최소한의 메모리는 얼마인가?"
에 대해 고민하기 시작한다.
메모리 접근은 순차적이고 지역화되어 있다는 특성 때문에, 어플리케이션을 실행하는데 완전한
10000바이트가 필요한 것처럼 보이지만, 사실 이 어플리케이션을 실행하는데 필요한 메모리 용량은
10000바이트보다 훨씬 적게된다.
즉, 위에서 말한 10000바이트의 프로그램이 실행하는데 필요한 최소한의 처리 순서를 다음과 같이 가정해보자.
1) 메모리에서 명령어를 읽어온다.
2) 명령어가 필요로하는 데이터도 메모리에서 읽어온다.
3) 명령이 완료된 후, 결과가 메모리에 기록된다.
예를 들어, 이 각각의 명령처리에 100바이트 씩의 메모리가 할당되어야 한다면, 고작 300바이트만으로 프로그램을 실행 시킬 수 있다는 것이다. 즉, 프로그램 실행과 상관있는 300바이트만 메모리에 올리면 된다는 뜻이다.
그렇다면 프로그램의 나머지 기능을 수행할 코드나 데이터부분의 처리를 위한 메모리 할당은 어떻게 해야 되나?
바로, 보조기억장치(하드디스크 등)이다.
현재 필요하거나 향후 필요한 어플리케이션의 일부분을 필요한만큼 계속 RAM에 올려놓도록 가상 메모리 하부 시스템을 구축하여 동작 할 수 있다면, 하드디스크를 RAM의 보조기억장치로 쓰는데 아무 문제가 없는 것이다.
이러한 방식은 빠르고 작은 기억장치를 크고 느린 기억장치와 병합하여 하나의 크고 빠른 기억장치처럼 작동하게 한다는 면에서 캐시(Cache)와 RAM의 관계와도 유사하다고 할 수 있다.
* 가상 주소 공간(Virtual Address Space)
가상 주소 공간이란 어플리케이션이 사용 가능한 최대 주소 공간을 말한다.
(가상 메모리 시스템에서 사용하는 주소공간이란 의미가 있다)
CPU아키텍처에 따라 주소지정을 위해 필요한 비트수가 달라지기 때문에 가상 주소 공간은 아키텍처에 따라 달라지게 된다. CPU 아키텍처는 레지스터(Register)의 크기와 관련이 있다.
예를 들어, 32비트 CPU 2^32 = 4294967296 로 0 ~ 4294967296까지의 주소공간을 나타낼 수 있다.
여기에 메모리의 가장 작은 단위는 Byte이기 때문에 4GByte가 되는 것이다.
때문에 각 어플리케이션들은 4GB의 주소 공간을 가질 수 있고, 32비트 시스템의 운영체제에서 RAM이 4GB까지 인식 못하는 이유도 여기에 있다.
또한, 운영체제에 따라서도 달라지게 된다.
32비트 윈도우에서는 기본적으로 응용프로그램에 4GB의 가상 주소 공간이 주어진다.
이중에서 2GB(0x00000000 ~ 0x7FFFFFFFF)는
사용자모드공간(USER MODE SPACE, 응용프로그램마다 독립적으로 사용가능한 공간)이고,
나머지 2GB(0x80000000 ~ 0xFFFFFFFFF)는
운영체제에서 사용하는 커널가상주소공간(KERNEL MODE SPACE)이다.
반면, 리눅스에서는 각 응용 프로그램이 4GB의 가상 주소 공간을 갖는 것은 같지만,
3GB의 사용자공간과 1GB의 커널공간을 갖게 된다. 커널 공간은 모든 프로세스들이 공유하게 된다.
프로그래머들이 프로그램을 만들 때 사용하는 주소들은 가상 주소 공간의 가상 주소들이고
실제 물리적인 메모리의 주소는 아니다. 이 것을 가능하게 해주는 녀석이 MMU 다.
**참고**
다음은 프로세스 및 커널의 가상 공간 구성에 대한 그림이다.
- Text Area
CPU가
실행하는 명령어로 구성되는 영역으로 읽기 전용으로만 접근이 허용되므로 프로세스의 실행 중 변화가 없다. 메모리 퇴출 시에도 디스크 파일에 존재한다.
- Data Area
초기화된
데이터 영역과 초기화되지 않은 데이터 영역으로 구분되며 메모리 적재 후 변화된 부분은 메모리 퇴출 시에 swap공간으로 가게 된다.
- Heap Area
프로세스
실행 중에 malloc, new
등의 동적 메모리 할당 요구를 수용하는 데이터 영역 공간이다. break. 값에 의해 그 끝을 나타내며 공간 부족 시에 주소가 커지는
방향으로 확장된다.
- Stack Area
함수
내에서 할당되는 automatic
변수들이나 함수 호출 시에 run-time
stack frame(함수가
호출 될 때의 반환주소,
환경정보 등) 등으로 할당되는 공간이다. 주소가 작아지는 방향으로 확장된다.
가상 주소(Virtual Address)와
MMU(Memory Management Unit, 메모리 관리 장치)
가상 주소(Virtual Address)는 논리 주소(logical Address)라고도 하며,
실제 메모리 상에서 유효한 주소는 물리 주소(Physical Address)또는 실주소(Real Address)라고 한다.
가장 주소 공간의 가상 주소는 MMU에 의해서 실제 물리 주소로 변환된다.
이 덕분에 프로그래머는 가상 주소 공간상에서 프로그램을 짜게 되어 프로그램이나 데이터가 주메모리 상에 어떻게 존재하는지를 의식 할 필요가 없다.
메모리 관리 하드웨어인 MMU가 없다면, CPU가 RAM에 접근 할 때 메모리 주소 123은 물리 메모리의 123 주소에 접근하게 될 것이다.
하지만 MMU를 사용하게 되면 메모리 주소 123은 물리 메모리 23892에 접근 할 수도 있고, 다음에는 12934에 접근할 수도 있다는 것이다.
그러나 수억만 바이트에 이르는 메모리를 하나씩 가상 주소에서 물리적 주소로 변역하게 되면 작업부하가 너무 높아지므로, MMU는 RAM을 여러 부분, 즉 일정한 크기를 가진 블록인 페이지(PAGE)로 나누어 각 페이지를 하나의 독립된 항목으로 처리하게 된다.
앞에서 설명한 대로, 10000바이트 가상 주소 공간을 가진 가상 어플리케이션의 첫 번째 명령어가 주소 12374에 저장된 데이터에 접근한다고 가정해보자.
그러나 컴퓨터는 오직 12000바이트의 물리적인 RAM만 존재하는 경우 CPU가 주소 12374에 접근을 시도한다면 어떠한 결과가 발생할까?
이러한 현상을 페이지 폴트(Page Fault)라고 부르는 것이다.
페이지 폴트(Page Fault)
페이지 폴트란 프로그램이 자신의 주소 공간(가상메모리공간)에는 존재하지만
시스템의 RAM에는 현재 없는 데이터나 코드에 접근 시도하였을 경우 발생하는 현상을 말한다.
페이지 폴트가 발생하면 운영체제는 그 데이터를 메모리로 가져와서 마치 페이지 폴트가 전혀 발생하지 않은 것처럼 프로그램이 계속적으로 동작하게 해준다.
앞에서 설명한 바와 같이 CPU는 우선 원하는 주소(12374)를 MMU에게 보내게 된다.
그러나 MMU는 주소 변환 과정에서 페이지 테이블(Page Table)에 이 주소에 대한 항목이 없다고 표시할 것이다.
그 다음 MMU는 CPU를 인터럽트 한 후, 페이지 폴트 처리기라는 소프트웨어가 실행되도록 한다.
이 페이지 폴트 처리기가 원하는 페이지를 디스크 상 어디에 위치하는지 찾은 후 읽어오게 하는 작업을 한다.
작업 세트(Working set)
현재 특수 프로세스 전용으로 할당된 물리적 메모리 페이지 그룹을 그 프로세스의 작업세트라고 한다.
메모리 완전 부족 현상을 방지하기 위해서는 프로세스의 작업 세트에서 페이지를 삭제하여 나중에 사용 가능한 여유 공간으로 변경시켜야 하는데, 운영 체제는 다음과 같은 방법을 사용하여 프로세스의 작업 세트를 줄여준다.
-수정된 페이지를 대용량 기억장치의 전용 공간(보통 스와핑(Swapping) 또는 페이징(Paging)공간이라고 부름)에 기록하기
-수정되지 않은 페이지는 여유 페이지로 표시함(이 페이지는 변경되지 않았으므로 디스크로 가져와 기록할 필요가 없다.)
또한, 운영체제는 모든 프로세스에 적절한 작업 세트를 할당하기 위하여 모든 페이지에 대한 사용 정보를 기록해야 하는데, 이렇게 함으로써
- 운영 체제는 어느 페이지가 자주 사용되었으며(이러한 페이지는 메모리에 계속 두어야 한다.)
- 어느 페이지가 사용되지 않았는지(따라서 메모리에서 삭제 할 수 있다) 결정할 수 있게 된다.
대부분의 경우 프로세스 작업 세트에서 최근 가장 자주 사용되지 않은 페이지를 찾아서 삭제하게 되어 있다.
# 참고로 페이지 하나의 크기는 x86, x64와 amd64에서는 4KB, ia64에서는 8KB의 크기를 가진다.
# 페이지의 동적 주소 변환
페이징 기법이 적용된 시스템에서 가상주소는 순서쌍(p,d)로 나타낼 수 있고, p는 가상 메모리 내에서 참조될 항목에 속해 있는 페이지 번호(page number)이고,
d는 페이지 p 내에서 참조될 항목이 위치하고 있는 곳의 거리(distance)이다.
과정은 다음과 같다.
1) 수행 중인 프로세스가 가상주소 V(p,d)를 참조한다.
2) 페이징 기법을 통해 페이지 p가 페이지 프레임 p'에 있음을 알아낸다.
3) 실주소 r = p' + d를 구한다.
# 스와핑(Swapping)
스와핑은 수정된 페이지를 하드 디스크와 같은 보조기억 장치 내부의 시스템 스왑 페이지에 기록하는 작업을 한다.
과정은 다음과 같다.
1) 프로세스에서 페이지가 스와핑 됨
2) 프로세스가 실행 가능 상태가 되어 스왑된 페이지에 접근 시도함
3) 페이지 폴트로 메모리로 다시 기록됨(이 때, 대부분의 경우 다른 프로세스 페이지가 스와핑되어 나가게 됨)
4) 얼마 지나지 않아 이 페이지가 다시 스와핑 됨
이러한 상황이 너무 자주 발생하면 더 많은 CPU 및 입/출력 작업을 무리하게 요구 당하게 되며, 프로그램의 처리 속도가 급격히 떨어지게 된다.
극단적인 경우엔 시스템은 아무런 작업도 실행하지 못 한채, 페이지를 메모리에서 가져오고 빼내는 작업에만 모든 자원을 소모하게 되는 경우도 발생할 수 있다.
이와 같은 경우를 쓰레싱(Thrashing)이라고 하며, 현재 작업을 실행하는데 RAM이 부족하다는 것을 나타낸다.
참조 : http://combiz119.tistory.com/entry/%EA%B0%80%EC%83%81= %EB%A9%94%EB%AA%A8%EB%A6%ACVirtual-Memory%EB%9E%80