KVM(Kernel-based Virtual Machine)
KVM은 리눅스 커널 모듈의 일종으로 리눅스 커널 자체를 일종의 하이퍼바이저 역할을 하게 해주는 기술이다.
라고만 들으면 리눅스 커널이 또 엄청난 일들을 하는 것 처럼 느껴지는 데, 그런 것은 아니고
하드웨어에서 가상화 지원을 하지 않으면 사용하지 못하는 기능이다.
가상화란 뭔가
가상화를 한 줄로 정의하자면
게스트한테 자기만의 하드웨어가 있다는 환상을 주면서(추상화), 실제론 호스트가 모든 것을 통제하는 것(격리)
두 가지가 핵심이다.
- 격리: 게스트가 호스트 / 다른 게스트를 침범하지 못하게 함
- 추상화: 게스트가 실제 하드웨어 구조를 몰라도 동작하게 만드는 것
내가 진짜 커널이야
게스트 OS의 커널도 소프트웨어이고 프로세스다 근데 CPU는 "이게 게스트 코드야, 호스트 코드야" 를 모름, 그냥 명령어네 실행해야겠다
CPU에는 보호 링 (Prorection Ring) 이라는 권한 레벨이 있다.
링 0 → 커널 (최고 권한, 특권 명령 실행 가능)
링 3 → 유저 애플리케이션이건 개념적인 게 아니고 CPU 하드웨어에 물리적으로 구현된 부분이다.
CPU가 명령을 실행할 때마다 현재 링을 확인하고 특권 명령을 낮은 링(권한이 낮은=유저 어플리케이션)에서 실행하려하면 트랩(예외)를 발생시킨다.
문제는 게스트 커널을 링 0에서 돌리면 어떻게 되냐
게스트 커널: "나 진짜 커널이야, 특권 명령 써도 돼"
↓
CPU: "링 0이네, 그냥 실행"
↓
호스트 제어권 상실
게스트가 물리 하드웨어 직접 접근 가능
다른 VM 메모리도 들여다볼 수 있음커널 위에 커널이 올라가면, 게스트 커널이 자기가 진짜 커널인 줄 알고 특권 명령을 막 날리기 때문에 제어가 안 된다. 이게 가상화의 근본적인 문제다.
세대별 가상화 기법
1세대 소프트웨어 애뮬레이션
가장 단순한 해결책으로 게스트 코드를 CPU에서 직접 실행하지 않는다.
소프트웨어가 게스트 명령어를 한 줄씩 읽고 해석해서 실행
for instruction in guest_code:
if instruction == 일반명령:
호스트_CPU에서_실행()
elif instruction == 특권명령:
소프트웨어_직접_처리() # CPU에 안 넘기고 가로챔CPU에 직접 안 넘기고 소프트웨어가 중간에서 다 걸러서 실행하는 방식이다.
안전하지만 명령어 하나 실행할 때마다 해석 과정이 필요하니까 느리다. 네이티브의 5~30% 수준.
2세대: 바이너리 변환 (VMWare , 1999)
x86에는 가상화의 구멍이 있었다.
게스트를 링 1/3으로 강등시키면 일부 특권 명령이 트랩을 발생시키지 않고 조용히 실패하거나 하드웨어 정보를 그대로 노출했다.
VMware가 이걸 소프트웨어로 해결한 방식이 바이너리 변환(BT)이다.
게스트 코드 실행 직전에 코드 블록을 스캔
↓
문제 명령어 발견 → 안전한 코드로 교체한 블록을 메모리에 생성
↓
원본 대신 교체된 블록 실행 후 캐시에 저장- 원본 파일은 건드리지 않는다. 메모리 위에서만 일어나는 일이다.
3세대: 반가상화
게스트 OS 커널을 수정해서 특권 명령 대신 hypercall 을 사용하도록 한다.
기존: 게스트가 특권 명령 실행 -> VMM(하이퍼바이저)이 가로챔 반가상화: 게스트가 처음부터 hypercall을 사용하여 하이퍼바이저에게 요청함
VMM이 가로챌 이유가 없으니 바이너리 변환 방식보다느 빠름.
- 단점은 게스트OS를 수정하야한다는 점임
4세대: 하드웨어 가상화
소프트웨어에서 해결하던 문제들을 하드웨어 레벨에서 처리하기 시작함.
기본적인 매커니즘은 아래와 같음
- CPU에서 게스트/호스트 두가지 동작 모드를 구분한다
- 기존 보호 링 구조를 없앤 것이 아니라 그위에 차원이 하나더 생김
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이 명령어를 해석하거나 실행하는 게 아니다)