관리 메뉴

맨땅에 코딩

QEMU 그게 뭔데? 본문

낙서장

QEMU 그게 뭔데?

나는 푸딩 2025. 10. 26. 20:37

졸려 죽겠지만 꾹 참고 QEMU 내부 구조에 대해 간단히 요약해봅니다....

우리 플러그인 개발 잘할 수 있겠지?

잘하고 싶다.

 

1. QEMU의 큰 그림

QEMU는 크게 2개의 층으로 구성된다.

설명
Front-end (System Emulation) 가상 머신 관리 (디스크, 네트워크, 장치, 스냅샷 등)
Back-end (CPU Emulation: TCG) 게스트 CPU 명령어를 해석하고 실행하는 핵심 엔진
┌──────────────────────────┐
│   QEMU Frontend Layer    │  ←  CLI 옵션, 장치 모델, 스냅샷, QMP
└─────────────┬────────────┘
              │
┌─────────────▼────────────┐
│   QEMU Backend (TCG)     │  ←  CPU 명령어 실행기 (Tiny Code Generator)
└─────────────┬────────────┘
              │
┌─────────────▼────────────┐
│     QEMU Plugin API      │  ←  우리가 만든 코드가 여기에 붙음
└──────────────────────────┘

 

2. QEMU의 실행 사이클(Main Loop)

while (vm_running) {
    poll_hardware_events();   // 디바이스 I/O, IRQ 확인
    execute_vcpu();           // 게스트 CPU 실행
    handle_timers();          // 타이머 인터럽트 처리
}

 

위 루프는 "호스트 CPU 위에서 게스트 OS를 계속 돌리는 메인 엔진"이다.

우리가 만들 플러그인은 주로 execute_vcpu() 내부에서 콜백될 것이다.

 

3. TCG(Tiny Code Generator)란?

게스트 CPU 명령어를 호스트 CPU 명령어로 번역해 실행하는 엔진

 

이것을 QEMU가 실시간으로 만들어 실행한다.

mov eax, ebx
→ TCG: load ebx → store eax
→ 호스트: mov rax, rbx

 

플러그인은 이 변환된 블록(Translation Block, TB) 단위로 감시할 수 있다.

 

4. Translation Block(TB)

QEMU는 CPU 명령어를 TB(Translation Block) 단위로 묶어서 번역·캐시한다.
[TB1] 0x400000 → 0x40000F
[TB2] 0x400010 → 0x40002A
[TB3] 0x40002B → 0x400045
  • TB는 "한 번 번역된 코드 블록"으로, 여러 번 재사용된다.
  • CPU가 TB를 실행하면, 그 안의 모든 명령어를 순서대로 수행한다.

플러그인에서는 2가지 수준에서 감시가 가능:

 

  • qemu_plugin_register_vcpu_tb_exec_cb() → TB 단위 콜백
  • qemu_plugin_register_vcpu_insn_exec_cb() → 명령어 단위 콜백

5. QEMU → Plugin 호출 타이밍

게스트 명령 실행
   ↓
QEMU TCG 변환
   ↓
TB 실행 시작        → [on_tb_exec_start()]
   ↓
각 명령어 실행      → [on_insn_exec()]
   ↓
메모리 접근 발생    → [on_mem_access()]
   ↓
IRQ / 예외 발생     → [on_interrupt()]
   ↓
TB 종료             → [on_tb_exec_end()]

 

 

우리가 짜는 플러그인 콜백들은 이 시점들에 정확히 연결된다.

즉, QMEU는 매 실행 루프마다 "어떤 이벤트가 발생했는지"를 플러그인에게 알려준다.

 

6. 플러그인 구조 개요

plugin_main.c
 ├─ qemu_plugin_install()      ← 플러그인 진입점
 ├─ register_exec_callback()   ← 명령어/메모리 콜백 등록
 ├─ on_exec_cb()               ← 명령어 실행 시 호출
 ├─ on_mem_cb()                ← 메모리 접근 시 호출
 └─ on_irq_cb()                ← 인터럽트 발생 시 호출

 

7. 실제 콜백 흐름 예시

void on_exec(unsigned int vcpu, uint64_t pc)
{
    printf("[EXEC] vCPU%d @0x%lx\n", vcpu, pc);
}

void on_mem(unsigned int vcpu, uint64_t addr, uint8_t is_store)
{
    printf("[MEM] vCPU%d %s @0x%lx\n",
           vcpu, is_store ? "WRITE" : "READ", addr);
}

void qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
                         int argc, char **argv)
{
    qemu_plugin_register_vcpu_insn_exec_cb(id, on_exec);
    qemu_plugin_register_vcpu_mem_cb(id, on_mem);
}

 

게스트 OS가 실제로 명령을 실행하거나 메모리에 접근할 때마다, 이 콜백들이 "호스트 콘솔"에 로그를 남긴다.

 

8. 플러그인 호출 타이밍 도식

QEMU 실행 루프
│
├─ poll_hardware_events()
│
├─ execute_vcpu()
│     ├─ TCG 번역 (TB 생성)
│     ├─ TB 실행 시작  →  [TB 콜백]
│     ├─ 명령어 실행   →  [Insn 콜백]
│     ├─ 메모리 접근   →  [Mem 콜백]
│     ├─ IRQ 발생      →  [IRQ 콜백]
│     └─ TB 종료       →  [TB end 콜백]
│
└─ handle_timers()

 

9. 이벤트별 플러그인 콜백 매핑

이벤트 QEMU 내부  플러그인 콜백
TB 생성 Translation Block 생성 qemu_plugin_tb_trans_cb
TB 실행 시작 TB 실행 시 qemu_plugin_register_vcpu_tb_exec_cb
명령어 실행 Instruction execution qemu_plugin_register_vcpu_insn_exec_cb
메모리 접근 load/store qemu_plugin_register_vcpu_mem_cb
인터럽트 발생 IRQ raise qemu_plugin_register_vcpu_interrupt_cb
예외 발생 Trap 발생 qemu_plugin_register_vcpu_exception_cb
CPU 상태 변경 Resume/Pause qemu_plugin_register_vcpu_state_cb

 

10. 플러그인 데이터 흐름 구조

[게스트 OS 실행]
      ↓
  QEMU 내부 이벤트
      ↓
  플러그인 콜백 호출
      ↓
  로그 수집 (Collector)
      ↓
  파일 저장 / 압축 (Storage)
      ↓
  분석 / 시각화 (Python 등)

 

QEMU는 이벤트 생성기, 플러그인은 이벤트 수집기, Python은 이벤트 분석기 역할