Virtual Machine

3 minute read

가상 머신

가상머신은 실제 존재하는 컴퓨터는 아니지만,
그 컴퓨터의 프로그램을 실행할 수 있게 소프트웨어로 구현한 가상의 컴퓨팅 환경이다.

운영체제 가상 머신

사용자들이 운영체제를 포함한 전체 컴퓨터를 자기 혼자서 쓰고 있다는 환상을 제공한다.
여러 개의 운영체제가 하드웨어 자원을 공유한다.

가상 머신 모니터

하이퍼바이저라고도 한다.
가상 머신을 지원하는 소프트웨어로
가상 자원을 물리 자원에 사상하며
손님 OS 는 VMM 위에서 사용자 모드 프로그램으로 동작한다.
그러나 이 손님 OS가 실제 메모리에도 접근하는 등의 보안 취약점이 발생할 수도 있다.

가상 메모리

이전엔 메인 메모리까지 가지 않기 위해 캐시를 썼다.
이번엔 하드 디스크 (혹은 SSD) 까지 가지 않기 위해 메인 메모리를 쓴다.

또한 가상 메모리를 사용하는 이유 :

  1. 다수의 프로그램을 동시 수행할 때 메모리를 효과적으로 공유할 수 있다.
    각 프로그램은 자기에게 할당딘 메모리만 읽을 수 있다.
    이들의 주소 공간은 한 번 변환을 거친 다음 실제 물리 주소로 갈 수 있다.

  2. 사용자 프로그램을 메인 메모리보다 더 크게 할 수 있다.
    가상 메모리는 2단계 메모리 계층이므로 필요한 부분만 메모리에 올라갈 수 있기 때문.

가상 메모리는 페이지라는 블록 단위로 나뉘어 메인 메모리에 올라간다.
이때 한 번에 많은 양 (4Kib) 을 가져온다.

설계 고려사항

캐시가 실패해서 메인 메모리까지 가는데에는 백 사이클 정도이나
메인 메모리가 실패해 디스크까지 가면 수백만 사이클의 손해를 본다.

따라서 페이지의 크기를 크게하고
페이지가 부재해서 디스크까지 내려가는 일을 최소화하고
설령 그렇더라도 이 처리를 효율적으로 쓰고
나중에 쓰는 방식으로 설계하자.

페이지 테이블

페이지는 메모리의 빈 공간에 막 들어간다.
그래서 그 페이지를 찾기 위한 테이블이 필요하다.
이는 시스템 소프트웨어에서의 페이지 테이블과 방식이 동일하다.

약간 다른 점이, MIPS의 가상 메모리에서 쓰이는 페이지 테이블은
인텔 x86 프로세서의 트리 구조가 아니고, 그 페이지의 구조도 다르다.

페이지 테이블의 시작 주소를 저장하는 레지스터를
페이지 테이블 레지스터라고 한다. (인텔에서의 CR3 레지스터와 비슷한 역할)

페이지 테이블을 이용한 주소 사상

가상 주소는 가상 페이지 넘버와 페이지 오프셋으로 이루어져 있다.

페이지 테이블 레지스터가 가리키는 페이지 테이블을 보고
자신의 가상 주소에 해당하는 페이지를 찾는다.
이때 가상 주소 공간은 2^32바이트, 실제 주소공간은 2^30 바이트로, 4배 차이가 난다.

페이지 부재

page fault 라고도 한다.
페이지 테이블을 찾아도 그 페이지가 없을 때 발생한다.
페이지 폴트가 발생하면 이를 interrupt 를 발생시켜 현재 프로세스 저장 후 정지한다.
그리고 내보낼 페이지를 선택한 후 디스크에서 해당 페이지를 찾아서 페이지 테이블에 올린다.
이를 기다리는 동안 프로그램은 정지상태로 기다린다. (로딩 중 … )
읽기가 끝나면 페이지 테이블을 수정하고 다시 운영체제가 자기 프로그램에게 할당해주길 기다린다.
할당되면 재개한다.

여기서 알 수 있듯이
쓰는 작업은 제일 나중에 미뤄서 꼭 필요할 때만 하는 나중 쓰기를 한다.

또한 교체해야 할 페이지를 찾을 때 쓰이는 갱신 비트는 (dirty bit)
메모리 적재 후 페이지의 변화가 생기면 1로 바꾼다.
갱신 비트가 1인 페이지는 메모리에서 교체될 때 디스크에 복사가 된다. (테이블에서 빠짐)

TLB

페이지 테이블을 위한 또 하나의 캐시가 있다.
페이지 테이블을 읽기 전에, Translation Lookaside Buffer 를 읽는다.
이는 최근에 사용된 페이지를 저장하고 그 변환을 추적한다.

적중시간은 0.5 ~ 1클럭사이클
실패 손실은 10 ~ 100 클럭 사이클에
실패율은 0.01 ~ 1% 정도이다.

굳이 페이지 테이블까지 안 가도 TLB 에서 끝나게 하는데에 목적이 있다.

가상 메모리, TLB와 캐시의 통합

가상주소를 가지고 TLB 에서 찾는다.
있으면 캐시로 간다.
 캐시에 있으면 그대로 쓴다. (0번)
 없으면 메인 메모리로 간다. (1번)

페이지 테이블로 간다.
 거기서 있으면 캐시로 간다.
  캐시에 있으면 그대로 쓴다. (1번)
  없으면 메인 메모리로 간다. (2번)

 없으면 페이지의 부재이다.

가상 메모리 보호 구현

사용자 프로그램 간에 페이지는 공유되면 안 된다.
각 프로세스의 가상 페이지를 서로 분리된 실제 페이지로 사상한다. (프로그램은 테이블을 저마다 하나씩 가지고 있다.)
서로간의 수정을 불허한다.

쓰기 접근 비트라는 것을 두어서
메모리에 쓸 때마다 이게 접근해도 되는 프로세스인지 확인한다.

운영체제가 보호를 위해 두 가지 모드를 마련한다.

  1. 사용자 프로세스를 실행하는 모드
  2. 운영체제 프로세스 실행하는 모드

사용자 프로세스는 읽을 수만 있고 쓰지는 못한다.
오로지 관리자 모드에서 사용 가능한 특수 명령어를 사용하여 쓴다.
이 모드를 바꿀 수 있는 메커니즘이 존재한다. (syscall 과 ERET)

요점 정리

메모리까지 안 가기 위한 캐시나, 디스크까지 안 가기 위한 페이지테이블-TLB나
네 가지 요소에 의해 그 종류가 결정된다.

  1. 블록을 어디에 넣을 것인가?
    한 곳일 수도, 몇 곳일 수도, 아무데나일 수도 있다.
    (직접 사상, 집합 연관 사상, 완전 연관 사상)

  2. 블록을 어떻게 찾는가?
    인덱스로 찾을 수도, 제한적 검색일 수도, 완전 검색일 수도, 별도의 참조 테이블일 수도 있다.
    (직접 사상, 집합 연관, 완전 연관, 페이지 테이블)

  3. 실패 시 어느 블록을 교체하는가?
    LRU 로 가장 나중의 쓰인 블록을 교체하든가, 랜덤으로 교체한다.
    LRU 의 구현은 2-way 를 넘어가면 어려워져서 랜덤으로 하기도 한다.
    사실 LRU 도 항상 맞는 것은 아니라 성능 차가 크지 않다.

  4. 쓰기는 어떻게 처리되는가?
    즉시 쓰기, 나중 쓰기
    페이징은 나중에 쓴다.