Files
document-haness/docs/virtualization/tech-log-studio/cpu-virtualization/concept/concept-kvm-vcpu-to-physical-cpu.md
T

22 KiB

id, kind, slug, title, topic, topicName, project, status, basisVersion, studio, assets, sourceRevision, source
id kind slug title topic topicName project status basisVersion studio assets sourceRevision source
5bcae89b-0873-4a2b-af4a-10e5234c085b CONCEPT kvm-vcpu-to-physical-cpu KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지 cpu-virtualization CPU 가상화 virtualization 초안 x86_64 Intel VMX(VT-x) · Linux KVM(kvm · kvm_intel) · QEMU/libvirt · VM 안의 K3s cgroup CPU limit https://hyeonworks.com/studio/documents/5bcae89b-0873-4a2b-af4a-10e5234c085b/edit
key file
vm-exit-handling-cycle ../../../final/assets/diagrams/vm-exit-handling-cycle/vm-exit-handling-cycle.svg
no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
final/document.md#1-이-문서의-범위
final/document.md#2-전체-구조
final/document.md#3-각-구성요소의-역할
final/document.md#4-vcpu와-vcpu-thread
final/document.md#5-host-linux-scheduler와-실제-cpu
final/document.md#6-kvm_run과-guest-실행
final/document.md#7-vm-entry와-vm-exit
final/document.md#8-무엇이-실제로-vm-exit을-발생시키는가
final/document.md#9-vm-exit-이후-처리
final/document.md#10-guest가-idle이면-물리-cpu는-어떻게-되는가
final/document.md#11-vm의-4-vcpu는-정확히-무엇을-의미하는가
final/document.md#12-cpu-contention과-overcommit
final/document.md#13-steal-time
final/document.md#14-실제-linux에서-확인할-수-있는-것
final/document.md#15-cpu-가상화-관점에서-장애를-보는-방법
final/document.md#16-현재-keycloak-k3s-실험과의-관계
final/document.md#18-bare-metal-k3s와-vm-기반-k3s의-차이
final/document.md#19-이-ssot에서-파생될-concept
final/document.md#22-현재-단계의-핵심-claim
final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제
final/document.md#25-cpu-문제를-계층별로-구분하는-진단표
final/document.md#26-현재-keycloak-실험에서-cpu-문제를-오판하지-않기-위한-기준
final/document.md#28-concept-->-open-question-->-case-적용-기준

KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지

virsh 로 가상 머신을 시작하면 그 뒤로 CPU 를 쓰는 쪽은 QEMU 프로세스다. vCPU 하나마다 QEMU vCPU 스레드가 하나씩 있다. 그 스레드를 호스트 Linux 스케줄러가 다른 호스트 스레드와 나란히 놓고 논리 CPU 에 배치한다. 게스트 코드는 그 스레드가 KVM_RUN 을 호출한 뒤 VM Entry 를 지나 물리 CPU 에서 직접 실행된다. 하이퍼바이저가 개입해야 하는 조건을 만나면 VM Exit 으로 KVM 에 제어권이 넘어간다. Keycloak 멀티 노드 실험을 가상 머신 두 대 위에서 돌리다 보니, 이 경로를 알아 두면 게스트 안의 지연을 애플리케이션·저장소 문제와 호스트 자원 문제로 갈라 볼 수 있다.

관계

  • pinning 없이 vCPU 스레드는 호스트 논리 CPU 사이를 옮겨 다니는가 이 글은 vCPU 스레드가 호스트 스케줄러에 따라 다른 논리 CPU 에서 실행될 수 있다고만 적었다. 그 이동을 PSR 로 실제로 재는 질문이다.
  • 실제 작업에서 주로 발생하는 VM Exit 은 무엇인가 무엇이 VM Exit 을 부르는지는 여기 적었지만, 어떤 작업에서 어떤 이유의 Exit 이 얼마나 나오는지는 이 호스트에서 세지 않았다.
  • 가상 머신 두 대에 동시에 부하를 주면 이 호스트에서 vCPU 경쟁이 실제로 생기는가 경쟁과 초과 할당은 예시 숫자로 설명했다. 이 테스트 환경의 논리 CPU 수와 두 가상 머신의 vCPU 합은 SSOT 에 적혀 있지 않다.
  • cgroup CPU 제한과 호스트 vCPU 경쟁을 지표로 가를 수 있는가 진단표는 두 원인을 다른 행으로 갈라 놓았다. 그 구분이 지표로도 갈리는지는 같은 크기의 지연을 두 원인으로 각각 재현해야 안다.
  • 운영 서버는 CPU 가상화 계층의 영향을 받는 구조인가 진단표의 어느 행부터 보는지가 이 답에 달려 있다.
  • 이 호스트의 NUMA 토폴로지는 가상 머신 성능을 고려해야 할 구조인가 이 글은 NUMA 를 진단표의 한 행으로만 두고 메모리 배치는 범위 밖으로 미뤘다. 단일 NUMA node 면 이 실험에서 우선순위를 낮추고, 다중이면 vCPU 와 메모리 배치를 따로 확인한다.

본문

virsh 뒤에서 가상 머신을 실행하는 프로세스

virsh 는 libvirt 기반 가상 머신을 관리하는 명령줄 도구다. virsh start ubuntu-vm 을 실행하면 명령이 libvirt 로 전달되고 libvirt 가 QEMU 프로세스를 실행한다. 명령 전달이 끝나면 virsh 자체는 종료될 수 있고, 가상 머신을 실제로 실행하는 QEMU 프로세스는 계속 살아 있다.

libvirt 는 가상 머신의 수명 주기와 구성을 관리하는 계층이다. 가상 머신 정의에는 RAM 8 GiB, vCPU 4 같은 값이 들어 있다. libvirt 는 그 정의를 바탕으로 QEMU 를 적절한 옵션과 함께 실행하고 관리한다.

QEMU 는 호스트의 사용자 공간에서 실행되는 프로세스다. 4 vCPU 가상 머신이라면 메인·제어 스레드 하나와 vCPU 스레드 넷이 만들어진다. KVM 가속을 쓸 때 게스트의 일반 CPU 명령을 QEMU 가 하나씩 소프트웨어로 번역해 실행하는 경로가 핵심은 아니다. vCPU 스레드가 KVM 을 통해 게스트 실행을 요청하면 게스트 코드는 VMX 를 이용해 실제 CPU 에서 직접 실행된다.

/dev/kvm 은 프로세스가 아니라 Linux 가 사용자 공간 프로그램에 KVM API 를 노출하는 문자 장치(character device) 인터페이스다. QEMU 는 이 파일을 열고 ioctlKVM_CREATE_VM, KVM_CREATE_VCPU, KVM_SET_USER_MEMORY_REGION, KVM_RUN 같은 동작을 요청한다.

커널 안쪽은 두 층으로 갈린다. KVM Core 가 CPU 제조사에 독립적인 공통 가상화 로직을 맡고, 제조사별 구현은 kvm_intelkvm_amd 로 나뉜다. kvm_intel 은 Intel CPU 환경에서 KVM 이 Intel 의 하드웨어 가상화 기능을 쓸 수 있게 하는 커널 모듈이다. AMD 환경에서는 kvm_amd 가 SVM 을 쓴다.

VMX(Virtual Machine Extensions)는 Intel CPU 자체가 제공하는 하드웨어 가상화 기능이어서 프로세스도 커널 모듈도 아니다. VMX 는 실행 영역을 호스트·하이퍼바이저 쪽인 VMX Root Operation 과 게스트 쪽인 VMX Non-Root Operation 으로 구분한다. 여기서 Root 는 Linux 의 root 사용자와 무관하다. 게스트 Linux 에서 root 권한으로 프로그램을 실행하더라도 게스트 전체는 VMX 관점에서 여전히 Non-Root Operation 에서 실행된다.

vCPU 하나마다 QEMU 스레드 하나

가상 머신에 4 vCPU 를 설정하면 게스트 OS 는 그것을 자신의 CPU 처럼 인식한다. 호스트에는 각 vCPU 의 실행 주체에 대응하는 QEMU vCPU 스레드가 하나씩 존재한다. 4 vCPU 는 게스트 OS 가 최대 4개의 CPU 실행 흐름을 가질 수 있도록 4개의 가상 CPU 실행 컨텍스트를 제공한다는 의미에 가깝다. 호스트의 물리 CPU 4개가 그 가상 머신으로 영구히 넘어가지는 않는다.

vCPU 스레드가 실행될 논리 CPU 를 특정 집합으로 제한하는 설정을 CPU pinning 이라고 한다. pinning 도 별도의 CPU 격리도 하지 않았다면 vCPU 스레드는 호스트 Linux 스케줄러가 스케줄링한다. 호스트가 6 Core / 12 Thread 라면 Linux 에서는 일반적으로 12개의 논리 CPU 가 스케줄링 대상으로 보인다. vCPU 스레드도 Chrome 스레드나 Java 스레드와 마찬가지로 그 12개 위에 배치된다. 그래서 시간에 따라 같은 스레드가 서로 다른 논리 CPU 에서 실행될 수도 있다. 다만 이 호스트에서 그 이동을 재지는 않았다. 스레드가 최근 실행된 논리 CPU 를 알려 주는 PSR 값을 시간에 따라 여러 번 보면 확인되는데, 그 관찰을 아직 돌리지 않았다.

호스트 CPU 가 부족하면 QEMU vCPU 스레드와 호스트의 다른 작업이 같은 논리 CPU 자원을 두고 경쟁한다.

KVM_RUN 이 게스트 실행을 시작한다

QEMU 의 vCPU 스레드가 게스트 vCPU 를 실행하려면 KVM 에 KVM_RUN 을 요청한다.

ioctl(vcpu_fd, KVM_RUN, 0);

KVM 은 이 요청을 받아 VM Entry 로 CPU 가 게스트 실행을 시작하거나 재개하게 한다. 이 상태에서 ADD, MOV, SUB, CMP, JMP 같은 게스트의 일반 명령은 실제 CPU 에서 직접 실행된다. 명령 하나마다 QEMU 까지 돌아갔다가 다시 실행하는 구조가 아니다.

무엇이 VM Exit 을 부르는가

VM Exit 은 가상 머신 종료가 아니다. CPU 가 VMX Non-Root 에서 게스트를 실행하다가 하이퍼바이저가 개입해야 하는 조건을 만나면, 게스트 실행에서 빠져나와 VMX Root 쪽 KVM 으로 제어권을 넘긴다. QEMU 가 종료되지도, 가상 머신 메모리가 지워지지도 않는다. 필요한 처리가 끝나면 다시 VM Entry 를 통해 게스트 실행을 이어 간다.

무엇을 가로챌지는 Intel VMX 의 VMCS(Virtual Machine Control Structure)에 있는 VM-Execution Control 등이 정한다. 특권 명령이면 전부 VM Exit 이라거나 root 가 실행하면 VM Exit 이라는 규칙은 맞지 않는다. Exit 여부는 VMX 설정과 해당 동작의 종류에 따라 정해진다.

무엇이 실행되나 가로채는 설정과 그 이유
HLT 계열 게스트 커널이 유휴 경로로 들어갔다. HLT exiting 을 쓰면 KVM 이 vCPU 를 재울 수 있다
IN · OUT I/O port 접근을 QEMU 의 가상 장치 동작으로 처리하도록 설정할 수 있다
CPUID 게스트에게 보여 줄 CPU 모델과 기능이 호스트 CPU 와 달라 CPU 정보를 가상화한다
CR0 · CR3 · CR4 접근 VMX 설정으로 어떤 접근을 가로챌지 고른다. 모든 접근이 Exit 을 부르지는 않는다
RDMSR · WRMSR 특정 MSR(Model-Specific Register) 접근을 가로채도록 설정했을 때 발생한다
예외 Exception Bitmap 설정에 따라 게스트가 직접 처리할 수도, 하이퍼바이저가 가로챌 수도 있다
외부 인터럽트 호스트가 처리해야 할 물리 인터럽트가 들어왔고 인터럽트 설정이 그렇게 돼 있다

CR3 처럼 페이지 테이블과 관련된 레지스터는 게스트 커널도 자주 쓴다. 그런 접근까지 전부 가로채면 Exit 이 늘어나기 때문에 현대 가상화에서는 불필요한 Exit 을 줄이는 쪽으로 설정한다.

Exit 을 KVM 이 처리할 때와 QEMU 까지 돌아갈 때

VM Exit 이 발생하면 KVM 은 먼저 Exit Reason 을 확인한다. KVM 이 커널 안에서 처리할 수 있는 Exit 은 처리한 뒤 바로 게스트로 재진입한다. 사용자 공간에서 가상 장치를 흉내 내야 할 때처럼 사용자 공간 처리가 필요할 때만 KVM_RUN 이 반환된다. 그러면 QEMU 가 필요한 가상 장치 동작을 처리한 뒤 다시 KVM_RUN 을 호출해 게스트 실행을 재개한다.

게스트에서 VM Exit 으로 KVM 에 닿은 뒤 Exit Reason 에 따라 커널 처리와 QEMU 처리 두 갈래로 갈라지고, 어느 갈래든 VM Entry 를 지나 게스트 실행 재개로 다시 모이는 흐름도. 커널 갈래는 KVM 커널 처리 하나로 끝나고, QEMU 갈래는 KVM_RUN 이 반환된 뒤 사용자 공간에서 가상 장치를 처리하고 KVM_RUN 을 다시 호출한다.

VM Exit 자체는 정상적인 가상화 동작이어서 Exit 이 있다고 문제가 되지 않는다. 어떤 작업에서 하이퍼바이저가 개입해야 하는 Exit 이 지나치게 빈번하고 그 처리 비용이 커지면 성능에 영향을 줄 수 있다. 그래서 Exit 횟수만 세지 않는다. Exit 이 난 이유와 작업 종류, 처리한 곳이 KVM 인지 QEMU 사용자 공간인지, 애플리케이션 지연이 Exit 증가와 같은 시점에 나타나는지를 같이 본다.

게스트에 할 일이 없으면 물리 CPU 가 풀린다

게스트에 실행할 작업이 없으면 게스트 커널은 유휴 경로로 들어가고 HLT 계열 동작이 사용될 수 있다. KVM 과 VMX 가 HLT exiting 을 쓴다면 여기서 VM Exit 이 발생한다. KVM 은 vCPU 가 당장 할 일이 없는 상태를 처리하면서 그 스레드를 재우거나 대기 상태로 둘 수 있다. 그동안 호스트 스케줄러는 그 논리 CPU 를 호스트의 다른 작업에 쓴다.

나중에 타이머, 인터럽트, I/O 완료처럼 vCPU 를 다시 실행해야 할 이유가 생기면 vCPU 가 깨어나 실행 대기 상태가 된다. 호스트 스케줄러가 그 스레드를 논리 CPU 에 배치하면 KVM 이 VM Entry 로 게스트 실행을 재개한다. 가상 머신에 4 vCPU 를 설정했어도 그 가상 머신에 할 일이 없는 동안에는 호스트가 CPU 자원을 다른 작업에 쓴다.

CPU 가 모자랄 때 게스트 안에서 보이는 값

호스트에 논리 CPU 가 12개 있고 8 vCPU 가상 머신을 네 대 올리면 32 vCPU 가 12개의 논리 CPU 위에서 실행될 수 있다. 그 가상 머신들이 대부분 유휴 상태면 이 구성 자체가 장애를 뜻하지 않는다. 여러 가상 머신의 vCPU 가 동시에 실행 대기 상태가 될 때 CPU 경쟁과 스케줄링 지연이 늘어난다.

vCPU 스레드끼리만 경쟁하지도 않는다. 호스트에 Nginx 를 직접 설치하고 가상 머신 두 대를 실행하는 테스트 환경이라면 호스트 쪽 Nginx 와 K3s, DB, Redis, 그 밖의 호스트 프로세스도 같은 Linux 스케줄러를 거친다.

게스트 안에서 이 경쟁이 드러나는 값이 steal time 이다. 게스트 Linux 의 top 에서 %st 로 확인할 수 있다. 게스트 vCPU 에 실행할 작업이 있고 호스트에서 그 스레드가 CPU 를 필요로 하는데, 다른 작업 때문에 즉시 실행되지 못한 시간을 나타낸다. 이 값이 높으면 호스트 CPU 경쟁이나 논리 CPU 보다 많은 vCPU 를 할당한 초과 할당을 의심할 수 있지만, 단독으로 원인을 확정하는 지표는 아니다. 호스트 CPU 포화와 실행 대기열, 친화도, 어떤 작업이 돌고 있었는지를 함께 확인한다.

vCPU 스레드가 실행 대기 상태가 되어도 호스트 스케줄러가 실제 논리 CPU 에 배치할 때까지 기다릴 수 있다. 호스트가 포화될수록 이 대기 시간이 길어지고 게스트에서는 애플리케이션 지연 증가로 보인다. 그래서 vCPU 수를 늘린다고 항상 빨라지지 않는다. 게스트 쪽 작업이 그만큼의 병렬성을 쓰지 못하거나 호스트 전체 CPU 에 비해 vCPU 가 지나치게 많으면 스케줄링 대상만 늘어난다. pinning 도 잘못 설정하면 특정 CPU 에 작업이 몰리기 때문에 pinning 여부만 보지 않고 CPU 별 사용률과 친화도도 본다.

어느 계층에서 느려졌는지 가르는 표

가상 머신 안의 Keycloak 이 느려졌을 때 CPU 경로는 애플리케이션에서 물리 CPU 까지 여러 층을 지난다. Keycloak 파드는 K3s 의 cgroup CPU 상한 아래에서 실행된다. 그 아래로 게스트 Linux 와 vCPU, QEMU vCPU 스레드, 호스트 Linux 스케줄러, KVM 과 VMX, 물리 CPU 와 NUMA 가 이어진다. 같은 「CPU 가 느리다」는 현상도 층마다 원인이 다르다.

어떤 문제인가 주로 어느 계층인가 무엇부터 확인하나
게스트 CPU 포화 게스트 게스트 CPU, 프로세스·스레드, load
CPU 제한(throttling) K3s / cgroup CPU 상한, throttled time
vCPU 과다 할당 가상 머신 구성 vCPU 수, 작업 병렬성
CPU 초과 할당 호스트 구성 전체 vCPU, 호스트 논리 CPU
CPU 경쟁 호스트 스케줄러 호스트 CPU, 실행 대기열, CPU 별 사용률
steal time 증가 게스트에서 관측 %st, 호스트 경쟁
스케줄링 지연 호스트 스케줄러 실행 대기열, 스케줄러 관찰
잘못된 pinning 호스트 / 가상 머신 설정 친화도, CPU 별 사용률
과도한 VM Exit KVM / VMX Exit 횟수와 이유, 작업
호스트 CPU 포화 호스트 호스트 CPU·load·실행 대기열
NUMA locality 하드웨어 / 메모리 NUMA 토폴로지, CPU·메모리 배치

이 표로 하나의 지표를 골라 원인을 확정하지 않는다. 어느 계층부터 조사할지 범위를 줄이는 데 쓴다.

특히 두 원인이 겹쳐 보인다. 호스트 CPU 를 제때 받지 못하는 문제는 경쟁과 steal time, 스케줄링 쪽이고, 파드가 자신에게 걸린 CPU 상한을 넘겨 제한되는 문제는 K3s cgroup 쪽이다. 호스트 CPU 에 여유가 있어도 Keycloak 파드는 설정된 상한 때문에 실행이 제한될 수 있다. cgroup 제한은 KVM 문제가 아니지만 가상 머신 안에서 K3s 를 운영하는 이 실험에서는 같은 애플리케이션 지연으로 관찰되기 때문에 진단 경계에 포함한다.

호스트 OS 에 K3s 를 직접 설치했다면 컨테이너의 프로세스는 호스트 커널을 공유하고 호스트 스케줄러가 직접 스케줄링하는 대상이어서 QEMU 와 /dev/kvm, KVM, VMX 를 지나지 않는다. 가상 머신 안에 K3s 를 구성하면 게스트 Linux 와 vCPU, QEMU vCPU 스레드, KVM 과 VMX 계층이 그 아래에 더해진다. 같은 부하 테스트라도 가상 머신 기반 환경에서는 호스트 가상화 자원 병목을 추가로 확인한다. 운영 서버에서는 위 표의 어느 행부터 보는지가 달라질 수 있다. 운영 서버가 호스트에 K3s 를 직접 설치한 구성인지 상위 하이퍼바이저나 클라우드 가상 머신 위에 있는지를 아직 확인하지 않았기 때문이다. 부하를 걸어 재야 아는 것이 아니라 서버 구성을 한 번 확인하면 답이 정해진다.

호스트에서 이 경로를 확인하는 명령

이 경로가 실제 호스트에서도 그대로인지는 명령으로 하나씩 확인할 수 있다. CPU 가 하드웨어 가상화를 지원하는지는 플래그로 본다.

grep -E 'vmx|svm' /proc/cpuinfo

커널 모듈은 lsmod | grep kvm 으로, KVM API 진입점은 ls -l /dev/kvm 으로 확인한다. 실행 중인 가상 머신은 virsh list 로 본다. ps -ef | grep '[q]emu' 를 실행하면 가상 머신이 살아 있는 동안 함께 남아 있는 쪽이 virsh 가 아니라 QEMU 프로세스임을 확인할 수 있다.

QEMU 프로세스의 스레드는 ps -T -p <QEMU_PID>top -H -p <QEMU_PID> 로 본다. 스레드 이름은 환경과 QEMU 버전에 따라 다를 수 있지만 vCPU 관련 스레드를 호스트에서 관찰할 수 있다. 그 스레드가 최근 어느 논리 CPU 에서 실행됐는지는 PSR 열이 알려 준다.

ps -eLo pid,tid,psr,pcpu,comm | grep qemu

여기 보이는 값은 vCPU 가 물리 CPU 에 영구 고정돼 있다는 뜻이 아니라, pinning 을 하지 않았을 때 스케줄링에 따라 달라지는 값이다. 게스트 쪽에서는 top 등으로 steal time 을 확인하고, 환경이 지원하면 perf kvm 으로 KVM 관련 실행 통계를 본다.

sudo perf kvm stat live

지원되는 명령과 표시되는 Exit 이유는 커널과 perf 버전, CPU 아키텍처 및 설정에 따라 다르기 때문에 그 환경에서 무엇이 되는지 perf kvm --help 로 확인한다. 필요하면 KVM tracepoint 를 이용한 별도 추적도 검토한다.

이 문서가 확정하지 않는 것

CPU 가상화 개념에서 확정하는 수준은 셋까지다. 구조적으로 어떤 문제가 발생할 수 있는가, 어떤 지표로 그 문제를 의심할 수 있는가, 어느 계층에서 확인해야 하는가. 현재 테스트 서버에서 실제로 그 문제가 일어나는지는 여기서 사실로 확정하지 않는다.

이 호스트에서 잰 값은 하나도 없다. 관찰 명령은 무엇을 볼 수 있는지 적어 둔 목록이다. VM Exit 분포도 steal time 도, pinning 없이 vCPU 스레드가 논리 CPU 사이를 옮겨 다니는지도 아직 측정하지 않았다. 검증은 VMX 와 SVM 확인에서 시작한다. 그다음 KVM 모듈과 /dev/kvm, virsh 가상 머신, QEMU 프로세스, vCPU 스레드, 호스트 논리 CPU 스케줄링, 게스트가 유휴일 때와 부하를 받을 때의 비교, steal time, VM Exit 관찰로 이어진다. 값이 나오면 각각 재현 조건을 붙인 기록으로 남기고, 그때도 답하지 못한 항목은 열린 질문으로 유지한다.

범위 밖으로 미룬 영역도 있다. 네트워크·메모리·스토리지 가상화와 PCIe/VFIO/IOMMU 상세, K3s 네트워크와 컨테이너 런타임 상세는 이 문서가 다루지 않는다. NUMA 는 문제가 생길 수 있다는 것과 CPU 친화도와의 연관까지만 적었다. 메모리 배치와 NUMA 튜닝은 메모리 가상화 개념 문서에서 다룬다.

Keycloak refresh token 경쟁도 CPU 가상화 문제와 같은 문제가 아니다. 다만 가상 머신 기반 실험 환경의 CPU 경쟁이 실험 결과를 왜곡할 수 있어서 두 문제를 분리해 측정한다. 그래서 그 경쟁을 검증하는 첫 실험에서는 CPU 자원을 여유 있게 유지한 채 같은 세션과 같은 refresh token 에 대한 동시 요청을 만든다. 트래픽을 올려 자원 포화를 보는 부하·스트레스 실험은 따로 돌린다. 나눠 돌리는 이유는 결과를 「동일 상태에 동시에 접근해서 발생한 문제」와 「시스템 자원이 부족해져서 발생한 문제」로 갈라 읽기 위해서다. 한 실험에서 둘을 같이 일으키면 원인을 분리하기 어려워진다.