📢 Timer(Physical, Virtual)에 대해 알아봐요
하루 8시간 책상에 앉아 공부를 했을 때 정말 내 순수 공부 시간이 8시간이 맞을까요?
딴생각 안 하고 집중해서 공부를 한 순수 공부시간이 의미 있는 게 아닐까요?
Physical Timer와 Virtual Timer도 위와 유사한 개념이에요
무심히 흐르는 벽시계, System Counter
모든 코어가 공유하는 System Counter(CNTPCT_EL0)는 멈추지 않고 계속 증가해요
이 카운트를 각 코어의 Physical Comparator가 감시하다 비교 값에 도달하면 “띠링!” 하고 Physical IRQ를 발생시켜요
(Virtual IRQ도 마찬가지죠)
+------------------------+
| System Counter | (CNTPCT_EL0)
+-----------+------------+
|
CNTPCT_EL0 >= CNTP_CVAL_EL0 | (CNTPCT_EL0 – CNTVOFF_EL2) >= CNTV_CVAL_EL0
+---------------+---------------+
| |
+-----------v-----------+ +-----------v-----------+
| Physical Comparator | | Virtual Comparator |
+-----------+-----------+ +-----------+-----------+
| |
v v
pIRQ line vIRQ line
Physical, Virtual Timer 각각 Comparator와 CVAL 레지스터가 존재해요
가상 머신 등장, Hypervisor와 두 vCPU
Hypervisor가 vCPU0와 vCPU1을 번갈아 스케줄링해요
물리 시간이 4ms 흘렀을 때, 실제 각 vCPU는 2ms씩 수행됐죠
Wall-clock: 0 ----- 1 ----- 2 ----- 3 ----- 4 (ms)
Schedule: [vCPU0] [vCPU1] [vCPU0] [vCPU1]
여기서 질문!
① vCPU0가 T=0에 “3ms 뒤에 IRQ”를 예약했으면, 4ms 시점에 IRQ가 올까?
- 답 : physical timer를 쓰면 가능 vtimer 쓴다면 안 돼요;;
② “내가 실제로 2ms 뛰었을 때” IRQ가 오길 바란다면?
- 답 : vtimer를 쓰면 되죠!
이를 위해 두 종류의 타이머를 제공합니다. (아래 내용을 먼저 숙지하고 드래그하여 답을 확인해 봐요)
EL1에서 쓰는 두 가지 타이머
| 타이머 종류 | 레지스터 | 시간 기준 |
| Physical Timer | CNTP_TVAL_EL0, CNTP_CTL_EL0 | 벽시계 시간 |
| Virtual Timer | CNTV_TVAL_EL0, CNTV_CTL_EL0 | “vCPU 실행 시간” |
Physical Timer은 그대로 System Counter를 보고
Virtual Timer은 System Counter에서 Offset만큼 빼서 봐요
- Virtual Count(CNTVCT_EL0) = CNTPCT_EL0 − CNTVOFF_EL2
+------------------------------+ +------------------------------+
| Multi-core Processor | | Multi-core Processor |
| | | |
| +-----------+ +-----------+ | | +-----------+ +-----------+ |
| | Core 0 | | Core 1 | | | | Core 0 | | Core 1 | |
| +-----┳-----+ +-----┳-----+ | | +-----┳-----+ +-----┳-----+ |
| +-----┴-----+ +-----┴-----+ | | +-----┴-----+ +-----┴-----+ |
| | phy | vir | | phy | vir | | | | phy | vir | | phy | vir | |
| +-----------+ +-----------+ | | +-----------+ +-----------+ |
| | Cmptr 0 | | Cmptr1 | | | | Cmptr 0 | | Cmptr1 | |
| +-----┳-----+ +-----┳-----+ | | +-----┳-----+ +-----┳-----+ |
+-------|--------------|-------+ +-------|--------------|-------+
└──────────────┤ ├──────────────┘
│ │
+-----┴----------------------┴-----+
| Counter Module |
+----------------------------------+
“내가 뛴 시간”만 세는 비밀, CNTVOFF_EL2
vCPU context switch out 순간: “여태까지 내가 본 가상 시간” 저장
이 vCPU가 잠시 멈추기 직전, 아래 코드를 통해 지금까지 누적된 가상 시간(saved_virt)을 읽어서 보관해요
saved_virt = read(CNTVCT_EL0);
vCPU context switch in 순간: 새 오프셋(offset) 계산
vCPU를 다시 실행시킬 때, 아래와 같이 OFFSET 계산해요
phys_now = read(CNTPCT_EL0); // 지금 벽시계(Physical) 시간
new_offset = phys_now − saved_virt; // “쉬고 있던 시간”을 반영한 오프셋
write(CNTVOFF_EL2, new_offset);
그 뒤 가상 타이머가 어떻게 흘러갈까?
가상 타이머는 System timer에서 Offset을 뺀 값이 돼요
CNTVCT_EL0 = CNTPCT_EL0 − CNTVOFF_EL2
즉, 마지막 saved_virt 시점부터 타이머가 이어진 것처럼 동작하죠
예제로 살펴보면
+------------+------+------+------+------+------+------+
| Time (ms) | 0 | 1 | 2 | 3 | 4 | 5 |
+------------+------+------+------+------+------+------+
| Scheduled | vCPU0| vCPU1| vCPU0| vCPU1| vCPU1| vCPU0|
+------------+------+------+------+------+------+------+
| PhysCount | 0 | 1 | 2 | 3 | 4 | 5 | <-- CNTPCT_EL0
+------------+------+------+------+------+------+------+
| Offset | 0 | 1 | 1 | 2 | 2 | 3 | <-- CNTVOFF_EL2
+------------+------+------+------+------+------+------+
| VirtCount | 0 | 0 | 1 | 1 | 2 | 2 | <-- CNTVCT_EL0
+------------+------+------+------+------+------+------+
결론, 두 가지 선택지
EL1의 Guest OS에서는 두 가지 타이머 중 하나를 선택
벽시계 기준(Physical Timer)
→ CNTP_TVAL_EL0에 3ms 설정 → 3ms 벽시계 후 IRQ
vCPU 기준(Virtual Timer)
→ CNTV_TVAL_EL0에 2ms 설정 → vCPU가 2ms 뛰었을 때 vIRQ
가장 중요한 Offset 로직만 구현해 두면 모든 vCPU가 “내가 뛴 시간만 세는 타이머”를 갖게 돼요
Reference
https://developer.arm.com/documentation/102142/0100/Virtualizing-the-generic-timers
Documentation – Arm Developer
developer.arm.com
'TIL > 2025' 카테고리의 다른 글
| Macbook에 homebrew 설치 하기 (6) | 2025.05.03 |
|---|---|
| Mac book에 git 설치하기 (1) | 2025.05.01 |
| Virtualizing Exceptions in the Arm Architecture AArch64 (0) | 2025.04.29 |
| Trapping and Emulation of instructions (0) | 2025.04.29 |
| Memory Management Unit (MMU)의 Stage 2 translation (0) | 2025.04.29 |