가상화 기술에서는 CPU와 메모리 가상화를 다룬다. CPU 가상화는 "제한된 직접 실행(LDE)" 메커니즘을 사용하여 소프트웨어가 직접 하드웨어를 제어하게 하되, 시스템 콜과 타이머 인터럽트 등 핵심적인 시점에서 운영체제가 개입하여 하드웨어 제어를 유지한다. 이러한 방식으로 효율성과 제어성을 모두 달성할 수 있다.
메모리 가상화에도 비슷한 전략을 사용하여 효율성과 제어성을 동시에 확보하고, 소프트웨어 개발자가 주소 공간을 유연하게 사용할 수 있도록 지원해야 한다. 이를 위해 하드웨어 기술을 활용하여 메모리 주소 변환을 수행하고, 운영체제가 메모리를 관리하여 각 애플리케이션이 자신의 메모리만 접근하도록 제한한다.
이러한 작업의 목적은 각 프로그램이 자신의 전용 메모리가 있다는 환상을 만드는 것이다. 하지만 실제로는 여러 프로그램이 메모리를 공유하며 동작하고 있으며, 운영체제가 이를 효과적으로 관리하여 가상화된 메모리 공간을 제공한다.
1. Assumptions
메모리 가상화를 처음 시도할 때는 매우 간단하게 시작할 것이다. 사용자의 주소 공간이 물리적 메모리에 연속적으로 배치되어야 한다는 가정과, 주소 공간의 크기가 물리적 메모리의 크기보다 작아야 한다는 가정을 하게 될 것이다. 또한, 모든 주소 공간의 크기가 정확히 동일하다고 가정할 것이다. 이러한 가정이 현실적으로 보이지 않는다 해도 걱정하지 마라. 이후에는 이러한 가정을 완화하면서 메모리 가상화를 현실적으로 구현할 것이다.
2. An Example
주소 변환을 구현하려면 프로세스가 생성하는 모든 메모리 참조가 가상 주소 공간 내에 있어야 한다. 이를 위해 가상 주소를 물리 주소로 변환해야 한다. 예를 들어, 프로세스가 x라는 변수를 사용하는 코드를 실행할 때, 그 변수는 프로세스의 주소 공간의 어떤 위치에 있을 것이다. 그러나 이 주소 공간의 물리 메모리 내 위치는 0이 아닐 수 있다. 그렇다면 프로세스의 가상 주소 공간이 0에서 시작하는 것처럼 보이게 하는 방법은 무엇일까?
위의 예시에서, 프로세스의 가상 주소 공간의 시작 주소가 0이라면, x 변수의 주소는 15KB일 것이다. 그러나 실제로는 이 주소가 물리적 메모리 상의 어떤 위치에 매핑되어 있는지는 알 수 없다. 따라서, 운영체제는 프로세스의 가상 주소 공간을 물리적 메모리의 다른 위치로 이동시켜야 한다. 이를 위해 운영체제는 주소 변환을 통해 가상 주소를 물리 주소로 변환한다. 이를 통해 프로세스는 자신이 가상 주소 공간의 시작점인 0에서부터 메모리를 참조하는 것처럼 보일 수 있다.
예를 들어, 운영체제가 프로세스의 가상 주소 공간을 물리 메모리의 32KB에서 시작하도록 매핑했다면, 프로세스는 여전히 자신이 가상 주소 공간의 시작점인 0에서부터 메모리를 참조하는 것처럼 보일 수 있다. 위의 예시에서 프로세스의 코드 영역은 물리적 메모리의 32KB에서 40KB까지, x 변수는 물리적 메모리의 47KB에서 48KB까지 매핑된 것으로 나타나 있다. 이와 같은 방식으로, 주소 변환을 통해 프로세스가 가상 메모리 공간에서 자신의 주소 공간을 참조하는 것처럼 보이게 할 수 있다.
3. Dynamic (Hardware-based) Relocation
하드웨어 기반 주소 변환 기술을 이해하기 위해서는, 먼저 1950년대 후반의 첫 타임 쉐어링 기계에 도입된 base and bounds 기법이나 dynamic relocation 기법을 이해해야 한다. 이 기법은 CPU당 두 개의 하드웨어 레지스터(base register와 bounds register)가 필요하며, 이를 통해 프로세스의 주소 공간을 물리적 메모리 어디에든 위치시킬 수 있고, 프로세스가 자신의 주소 공간만 접근하도록 보장할 수 있다. 이런 설정에서 각 프로그램은 0번지에서 로드될 것처럼 작성되고 컴파일된다. 그러나 프로그램이 실행될 때 운영체제(OS)는 물리적 메모리에서 로드해야 할 위치를 결정하고, 이 값을 base register에 설정한다. 이 예에서 OS는 프로세스를 물리적 주소 32KB에서 로드하기로 결정하고, 이에 따라 base register를 이 값으로 설정한다.
프로세스가 실행 중일 때 메모리 참조가 발생하면, 하드웨어는 다음과 같이 변환한다:
물리적 주소 = 가상 주소 + base
프로세스에서 생성된 모든 메모리 참조는 가상 주소이며, 하드웨어는 이 주소에 base register의 내용을 추가하여 물리적 주소로 변환하고, 이 주소를 메모리 시스템에 전달할 수 있다.
이를 이해하기 위해 이전 예시에서 하나의 명령어를 추적해보자. 구체적으로, 이전에 본 시퀀스에서 하나의 명령어를 살펴보자.
128: movl 0x0(%ebx), %eax
프로그램 카운터(PC)가 128로 설정되면, 하드웨어는 이 명령어를 가져오기 위해 먼저 base register 값 32KB(32768)을 값에 추가하여 물리적 주소 32896을 얻은 다음, 해당 물리적 주소에서 명령어를 가져온다. 다음으로, 프로세서는 명령을 실행하기 시작한다. 어느 시점에서 프로세스는 가상 주소 15KB에서 로드를 발행하며, 프로세서는 이를 다시 base register(32KB)에 추가하여 최종 물리적 주소 47KB를 얻어서 원하는 내용을 가져온다.
가상 주소를 물리적 주소로 변환하는 것은 우리가 주소 변환이라고 부르는 기술과 정확히 일치한다. 이렇게 주소를 재배치하는 것은 프로세스가 실행 중일 때 발생하며, 프로세스가 실행 중일 때 주소 공간을 이동할 수 있으므로, 이 기술은 종종 dynamic relocation으로 참조된다. 그러나 이러한 기능을 수행하기 위해서는 base register와 bounds register와 같은 하드웨어 구조가 필요하며, 이들은 칩 안에 유지되는 하드웨어 구조로서, 하나의 CPU당 한 쌍씩 존재한다. 때때로 주소 변환을 돕는 프로세서의 일부를 메모리 관리 장치(MMU)라고 부르기도 한다. 이러한 주소 변환 기술이 발전함에 따라 MMU에 추가 회로를 추가할 수 있다.
마지막으로, bounds 레지스터에 대해 간략히 언급할 필요가 있다. 이는 두 가지 방식으로 정의될 수 있다. 하나는 위에서 본 대로, 주소 공간의 크기를 보유하고 있으며, 따라서 하드웨어는 base를 추가하기 전에 가상 주소를 먼저 bounds 레지스터와 비교하여 유효성을 검사한다. 두 번째 방법은 주소 공간 끝의 물리적 주소를 보유하며, 하드웨어는 먼저 base를 추가한 다음 주소가 bounds 내에 있는지 확인한다. 이 두 가지 방법은 논리적으로 동등하다.
4. Hardware Support: A Summary
이제 우리는 하드웨어가 제공해야 하는 지원을 요약해 보자(그림 15.3 참조). 먼저 CPU 가상화 챕터에서 설명한 것처럼, 두 가지 다른 CPU 모드가 필요하다. OS는 권한 모드(또는 커널 모드)에서 실행되며, 전체 시스템에 액세스할 수 있다. 반면 응용 프로그램은 사용자 모드에서 실행되며, 수행할 수 있는 작업이 제한된다. 특정 프로세서 상태 단어에 저장된 하나의 비트는 현재 CPU가 실행 중인 모드를 나타내며, 일부 특수한 경우(예: 시스템 콜 또는 다른 종류의 예외 또는 인터럽트)에 CPU는 모드를 전환한다.
하드웨어는 또한 base와 bounds 레지스터 자체를 제공해야 한다. 각 CPU는 추가적인 레지스터 쌍을 가지며, CPU의 메모리 관리 유닛(MMU)의 일부이다. 사용자 프로그램이 실행될 때, 하드웨어는 사용자 프로그램이 생성한 가상 주소에 base 값을 더함으로써 각 주소를 변환한다. 하드웨어는 또한 주소가 유효한지 여부를 확인할 수 있어야 하며, 이는 CPU 내부의 회로 및 bounds 레지스터를 사용하여 수행된다.
하드웨어는 OS가 다른 프로세스를 실행할 때 이를 변경할 수 있도록 base와 bounds 레지스터를 수정하는 특수한 명령어를 제공해야 한다. 이러한 명령어는 권한이 필요하며 커널(또는 권한) 모드에서만 레지스터를 수정할 수 있다. 사용자 프로세스가 실행 중일 때 임의로 base 레지스터를 변경할 수 있다면 어떤 혼란이 생길지 상상해 볼 수 있다. 그리고 그러한 어두운 생각은 악몽의 소재가 될 수 있으므로 빨리 마음에서 지워야 한다.
마지막으로, CPU는 사용자 프로그램이 메모리에 불법적으로 액세스하려고 할 때 예외를 발생시켜야 하며(주소가 "out of bounds"인 경우), 이 경우 CPU는 사용자 프로그램을 실행 중지하고 OS의 "out-of-bounds" 예외 핸들러를 실행하도록 해야 한다. 이 예외 핸들러에서는 해당 프로세스를 종료하는 것이 적절한 대응 방안일 것이다. 마찬가지로, 사용자 프로그램이 (권한이 필요한) base와 bounds 레지스터의 값을 변경하려고 시도하면 CPU는 예외를 발생시켜 "사용자 모드에서 권한이 있는 작업을 실행하려고 시도했습니다" 핸들러를 실행해야 한다. CPU는 또한 이러한 핸들러의 위치를 알리는 방법을 제공해야 하며, 몇 가지 더 권한이 필요한 명령어가 필요하다.
5. Operating System Issues
하드웨어가 동적 재배치를 지원하기 위해 새로운 기능을 제공하는 것과 마찬가지로, 운영체제(OS)는 이제 가상 메모리를 구현하기 위해 새로운 문제를 다루어야 한다. 이를 위해 OS는 프로세스가 생성될 때, 해당 주소 공간을 할당하고, 프로세스가 종료될 때는 해당 메모리를 회수하여 다른 프로세스나 OS에서 사용할 수 있도록 해야 한다. 또한, 프로세스 전환이 발생할 때는, CPU에 있는 base와 bounds 레지스터 쌍을 저장하고 복원하여 프로세스마다 다른 값을 사용할 수 있도록 해야 한다. 이를 위해 OS는 각 프로세스마다 자신의 공간을 스왑하여 새로운 메모리 위치에 저장할 수 있으며, 예외 핸들러도 제공해야 한다. 이러한 기능들은 OS와 하드웨어가 상호작용하는 타임라인에서 설명되어 있으며, 대부분의 경우, OS는 하드웨어를 적절히 설정하고 프로세스를 직접 CPU에서 실행시키며, 프로세스가 오작동하는 경우에만 OS가 개입하게 된다.