Skip to content

KVM(Kernel-based Virtual Machine) ​

KVM은 리눅스 커널 모듈의 일종으로 리눅스 커널 자체를 일종의 하이퍼바이저 역할을 하게 해주는 기술이다.

라고만 들으면 리눅스 커널이 또 엄청난 일들을 하는 것 처럼 느껴지는 데, 그런 것은 아니고

하드웨어에서 가상화 지원을 하지 않으면 사용하지 못하는 기능이다.

가상화란 뭔가 ​

가상화를 한 줄로 정의하자면

게스트한테 자기만의 하드웨어가 있다는 환상을 주면서(추상화), 실제론 호스트가 모든 것을 통제하는 것(격리)

두 가지가 핵심이다.

  • 격리: 게스트가 호스트 / 다른 게스트를 침범하지 못하게 함
  • 추상화: 게스트가 실제 하드웨어 구조를 몰라도 동작하게 만드는 것

내가 진짜 커널이야 ​

게스트 OS의 커널도 소프트웨어이고 프로세스다 근데 CPU는 "이게 게스트 코드야, 호스트 코드야" 를 모름, 그냥 명령어네 실행해야겠다

CPU에는 보호 링 (Prorection Ring) 이라는 권한 레벨이 있다.

bash
링 0  →  커널 (최고 권한, 특권 명령 실행 가능)
링 3  →  유저 애플리케이션

이건 개념적인 게 아니고 CPU 하드웨어에 물리적으로 구현된 부분이다.
CPU가 명령을 실행할 때마다 현재 링을 확인하고 특권 명령을 낮은 링(권한이 낮은=유저 어플리케이션)에서 실행하려하면 트랩(예외)를 발생시킨다.

문제는 게스트 커널을 링 0에서 돌리면 어떻게 되냐

bash
게스트 커널: "나 진짜 커널이야, 특권 명령 써도 돼"
    ↓
CPU: "링 0이네, 그냥 실행"
    ↓
호스트 제어권 상실
게스트가 물리 하드웨어 직접 접근 가능
다른 VM 메모리도 들여다볼 수 있음

커널 위에 커널이 올라가면, 게스트 커널이 자기가 진짜 커널인 줄 알고 특권 명령을 막 날리기 때문에 제어가 안 된다. 이게 가상화의 근본적인 문제다.

세대별 가상화 기법 ​

1세대 소프트웨어 애뮬레이션 ​

가장 단순한 해결책으로 게스트 코드를 CPU에서 직접 실행하지 않는다.

text
소프트웨어가 게스트 명령어를 한 줄씩 읽고 해석해서 실행
for instruction in guest_code:
    if instruction == 일반명령:
        호스트_CPU에서_실행()
    elif instruction == 특권명령:
        소프트웨어_직접_처리()  # CPU에 안 넘기고 가로챔

CPU에 직접 안 넘기고 소프트웨어가 중간에서 다 걸러서 실행하는 방식이다.
안전하지만 명령어 하나 실행할 때마다 해석 과정이 필요하니까 느리다. 네이티브의 5~30% 수준.

2세대: 바이너리 변환 (VMWare , 1999) ​

x86에는 가상화의 구멍이 있었다.
게스트를 링 1/3으로 강등시키면 일부 특권 명령이 트랩을 발생시키지 않고 조용히 실패하거나 하드웨어 정보를 그대로 노출했다.

VMware가 이걸 소프트웨어로 해결한 방식이 바이너리 변환(BT)이다.

text
게스트 코드 실행 직전에 코드 블록을 스캔
    ↓
문제 명령어 발견 → 안전한 코드로 교체한 블록을 메모리에 생성
    ↓
원본 대신 교체된 블록 실행 후 캐시에 저장
  • 원본 파일은 건드리지 않는다. 메모리 위에서만 일어나는 일이다.

3세대: 반가상화 ​

게스트 OS 커널을 수정해서 특권 명령 대신 hypercall 을 사용하도록 한다.

기존: 게스트가 특권 명령 실행 -> VMM(하이퍼바이저)이 가로챔 반가상화: 게스트가 처음부터 hypercall을 사용하여 하이퍼바이저에게 요청함

VMM이 가로챌 이유가 없으니 바이너리 변환 방식보다느 빠름.

  • 단점은 게스트OS를 수정하야한다는 점임

4세대: 하드웨어 가상화 ​

소프트웨어에서 해결하던 문제들을 하드웨어 레벨에서 처리하기 시작함.

기본적인 매커니즘은 아래와 같음

  • CPU에서 게스트/호스트 두가지 동작 모드를 구분한다
  • 기존 보호 링 구조를 없앤 것이 아니라 그위에 차원이 하나더 생김
text
root mode
    → KVM / 호스트가 실행되는 모드, 모든 권한
    └─ 링 0 (호스트 커널 / KVM)
    └─ 링 3 (QEMU 등 호스트 유저)

non-root mode
    → 게스트 OS가 실행되는 모드
    └─ 링 0 (게스트 커널) ← 특권 명령 실행 시 VM Exit 자동 발생
    └─ 링 3 (게스트 앱)
    ```

게스트 커널이 non-root 링 0에서 실행된다.
게스트 입장에선 자기가 진짜 커널인 줄 안다. 근데 non-root이니까 특권 명령을 날리면 CPU 회로(하드웨어)에서 VM Exit을 발생시킴.

그리고 제어권이 VMM으로 이동된다

그래서 사실 KVM은 이러한 하드웨어의 신호를 받아주고 적절히 처리한 뒤 다시 게스트로 돌려보내는 것이 전부임.(KVM이 명령어를 해석하거나 실행하는 게 아니다)