feat: 가상화 문서들 추가

This commit is contained in:
DongHyeonka
2026-09-10 08:54:05 +09:00
parent e9f6a93327
commit 43e1aadef0
695 changed files with 153404 additions and 12754 deletions
@@ -0,0 +1,30 @@
# Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다
## Alternative text
Guest 구역 안에서 Keycloak · PostgreSQL 의 GVA 가 Guest TLB 와 Guest Page Table 을 지나 GPA 가 되고, VM Boundary 를 넘어 EPT 에서 HPA 가 된 뒤 Host RAM 구역의 Host Physical Memory 를 지나 NUMA Node 0 과 NUMA Node 1 로 갈라지는 주소 변환 경로도.
## Long description
위에서 아래로 읽는다. 맨 위 Guest 구역 안에 Keycloak · PostgreSQL 이 있고 그 접근은 GVA 로 Guest TLB 에 들어간다. TLB 에 변환이 없으면 TLB Miss 로 Guest Page Table 을 조회하고 거기서 GVA 가 GPA 가 된다. 그 GPA 는 VM Boundary 를 넘어 Guest 구역 밖의 EPT 로 가고 EPT 가 GPA 를 HPA 로 바꾼다. HPA 는 Host RAM 구역의 Host Physical Memory 에 닿고, 그 physical page 는 맨 아래에서 NUMA Node 0 또는 NUMA Node 1 가운데 한쪽에 놓인다. 상자 안 작은 글씨는 그 단계가 어느 계층인지와 무엇을 무엇으로 바꾸는지를 적은 것이다. 어느 단계에서 접근이 완료되지 못할 때 무엇이 일어나는지와 QEMU 가 이 경로를 어떻게 준비하는지는 본문의 문단과 표가 맡는다.
## Elements and evidence
- **Boundary: Guest** (boundary): VM Boundary 안쪽. Guest Userspace 와 Guest Kernel 이 여기 있고 GVA 가 GPA 가 되는 첫 번째 변환이 여기서 끝난다. Evidence: L4034L4053.
- **Boundary: Host RAM** (boundary): 변환이 끝난 주소가 닿는 실제 서버 메모리. Evidence: L4069L4079.
- **Keycloak · PostgreSQL** (actor): Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다. Evidence: L4034L4038.
- **Guest TLB** (component): 최근 변환 결과를 담아 두는 CPU 안의 캐시. Evidence: L4043L4045.
- **Guest Page Table** (component): GVA 를 GPA 로 바꾸는 첫 번째 변환 표. Evidence: L4048L4053.
- **EPT** (component): VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다. Evidence: L4055L4067.
- **Host Physical Memory** (store): HPA 가 가리키는 실제 서버 메모리. Evidence: L4069L4072.
- **NUMA Node 0** (store): physical page 가 놓일 수 있는 한쪽 NUMA node. Evidence: L4073L4075.
- **NUMA Node 1** (store): physical page 가 놓일 수 있는 다른 쪽 NUMA node. Evidence: L4073L4075.
## Relationships
- **EPT → Host Physical Memory:** HPA. Evidence: L4062L4071.
- **Host Physical Memory → NUMA Node 0:** Physical Page. Evidence: L4071L4075.
- **Host Physical Memory → NUMA Node 1:** Physical Page. Evidence: L4071L4075.
- **Guest Page Table → EPT:** VM Boundary. Evidence: L4053L4062.
- **Keycloak · PostgreSQL → Guest TLB:** GVA. Evidence: L4036L4043.
- **Guest TLB → Guest Page Table:** TLB Miss. Evidence: L4043L4048.
@@ -0,0 +1,34 @@
# Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다
# Question: Guest 프로세스가 읽은 주소는 어느 계층을 차례로 지나 어떤 물리 메모리에 닿는가
direction: down
g0: "Guest" {
n0: "Keycloak · PostgreSQL" {
shape: person
}
n1: "Guest TLB" {
shape: rectangle
}
n2: "Guest Page Table" {
shape: rectangle
}
}
g1: "Host RAM" {
n4: "Host Physical Memory" {
shape: rectangle
}
n5: "NUMA Node 0" {
shape: rectangle
}
n6: "NUMA Node 1" {
shape: rectangle
}
}
n3: "EPT" {
shape: rectangle
}
g0.n0 -> g0.n1: "GVA"
g0.n1 -> g0.n2: "TLB Miss"
g0.n2 -> n3: "VM Boundary"
n3 -> g1.n4: "HPA"
g1.n4 -> g1.n5: "Physical Page"
g1.n4 -> g1.n6: "Physical Page"
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
<diagram id="guest-memory-address-translation-path" name="Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다">
<mxGraphModel dx="680" dy="1078" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="g_guest" value="Guest" style="swimlane;html=1;rounded=1;startSize=30;horizontal=1;dashed=1;strokeWidth=1.5;fillColor=#f7f9fb;strokeColor=#66788a;fontStyle=1;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="129.5" y="35.0" width="241.0" height="494.0" as="geometry"/>
</mxCell>
<mxCell id="g_host-ram" value="Host RAM" style="swimlane;html=1;rounded=1;startSize=30;horizontal=1;dashed=1;strokeWidth=1.5;fillColor=#f7f9fb;strokeColor=#66788a;fontStyle=1;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="40.0" y="737.0" width="420.0" height="296.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-process" value="Keycloak · PostgreSQL&lt;br/&gt;Guest Userspace" tooltip="Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다. | Evidence: L4034-L4038" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="159.5" y="81.0" width="181.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-tlb" value="Guest TLB&lt;br/&gt;Guest Kernel" tooltip="최근 변환 결과를 담아 두는 CPU 안의 캐시. | Evidence: L4043-L4045" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="175.0" y="248.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-page-table" value="Guest Page Table&lt;br/&gt;Guest Kernel&lt;br/&gt;GVA → GPA" tooltip="GVA 를 GPA 로 바꾸는 첫 번째 변환 표. | Evidence: L4048-L4053" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="175.0" y="415.0" width="150.0" height="88.0" as="geometry"/>
</mxCell>
<mxCell id="n_ept" value="EPT&lt;br/&gt;KVM / CPU&lt;br/&gt;GPA → HPA" tooltip="VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다. | Evidence: L4055-L4067" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="175.0" y="599.0" width="150.0" height="88.0" as="geometry"/>
</mxCell>
<mxCell id="n_host-physical-memory" value="Host Physical Memory" tooltip="HPA 가 가리키는 실제 서버 메모리. | Evidence: L4069-L4072" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="163.0" y="783.0" width="174.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_numa-node-0" value="NUMA Node 0" tooltip="physical page 가 놓일 수 있는 한쪽 NUMA node. | Evidence: L4073-L4075" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="943.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_numa-node-1" value="NUMA Node 1" tooltip="physical page 가 놓일 수 있는 다른 쪽 NUMA node. | Evidence: L4073-L4075" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="280.0" y="943.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="e_ept-to-host-memory" value="HPA" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_ept" target="n_host-physical-memory">
<mxGeometry relative="1" as="geometry">
<mxPoint x="274.0" y="735.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_host-memory-to-node-0" value="Physical Page" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-physical-memory" target="n_numa-node-0">
<mxGeometry relative="1" as="geometry">
<mxPoint x="193.0" y="867.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_host-memory-to-node-1" value="Physical Page" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-physical-memory" target="n_numa-node-1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="307.0" y="867.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_page-table-to-ept" value="VM Boundary" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-page-table" target="n_ept">
<mxGeometry relative="1" as="geometry">
<mxPoint x="274.0" y="551.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_process-to-tlb" value="GVA" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-process" target="n_guest-tlb">
<mxGeometry relative="1" as="geometry">
<mxPoint x="274.0" y="200.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_tlb-to-page-table" value="TLB Miss" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-tlb" target="n_guest-page-table">
<mxGeometry relative="1" as="geometry">
<mxPoint x="274.0" y="367.0" as="offset"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
@@ -0,0 +1,31 @@
{
"harness_version": "0.2.0",
"spec_id": "guest-memory-address-translation-path",
"spec_version": "1.1",
"spec_sha256": "9db86cb8c5f81b1c5886877e1efd3ae4d4174d839fb29cffd5f8ae102508f2f3",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"anchor": {
"kind": "heading",
"value": "87. 최종 기준 그림",
"line": 4029
}
},
"outputs": [
"guest-memory-address-translation-path.svg",
"guest-memory-address-translation-path.drawio",
"guest-memory-address-translation-path.mmd",
"guest-memory-address-translation-path.d2",
"guest-memory-address-translation-path.excalidraw",
"guest-memory-address-translation-path.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,22 @@
%% Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다
%% question: Guest 프로세스가 읽은 주소는 어느 계층을 차례로 지나 어떤 물리 메모리에 닿는가
flowchart TB
subgraph g_guest["Guest"]
n0(["Keycloak · PostgreSQL"])
n1["Guest TLB"]
n2["Guest Page Table"]
end
subgraph g_host_ram["Host RAM"]
n4["Host Physical Memory"]
n5["NUMA Node 0"]
n6["NUMA Node 1"]
end
n3["EPT"]
n0 -->|"GVA"| n1
n1 -->|"TLB Miss"| n2
n2 -->|"VM Boundary"| n3
n3 -->|"HPA"| n4
n4 -->|"Physical Page"| n5
n4 -->|"Physical Page"| n6
classDef external stroke-dasharray: 6 4
class n0 external
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1078" viewBox="0 0 680 1078" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다</title>
<desc id="diagram-description">위에서 아래로 읽는다. 맨 위 Guest 구역 안에 Keycloak · PostgreSQL 이 있고 그 접근은 GVA 로 Guest TLB 에 들어간다. TLB 에 변환이 없으면 TLB Miss 로 Guest Page Table 을 조회하고 거기서 GVA 가 GPA 가 된다. 그 GPA 는 VM Boundary 를 넘어 Guest 구역 밖의 EPT 로 가고 EPT 가 GPA 를 HPA 로 바꾼다. HPA 는 Host RAM 구역의 Host Physical Memory 에 닿고, 그 physical page 는 맨 아래에서 NUMA Node 0 또는 NUMA Node 1 가운데 한쪽에 놓인다. 상자 안 작은 글씨는 그 단계가 어느 계층인지와 무엇을 무엇으로 바꾸는지를 적은 것이다. 어느 단계에서 접근이 완료되지 못할 때 무엇이 일어나는지와 QEMU 가 이 경로를 어떻게 준비하는지는 본문의 문단과 표가 맡는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;guest-memory-address-translation-path&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;87. 최종 기준 그림&quot;,&quot;line&quot;:4029}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="1078" />
<rect class="group-box" x="129.5" y="35.0" width="241.0" height="494.0" rx="8" />
<rect class="group-label-bg" x="143.5" y="25.0" width="90.0" height="22" />
<text class="group-label" x="153.5" y="40.0">Guest</text>
<rect class="group-box" x="40.0" y="737.0" width="420.0" height="296.0" rx="8" />
<rect class="group-label-bg" x="54.0" y="727.0" width="90.0" height="22" />
<text class="group-label" x="64.0" y="742.0">Host RAM</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="250.0,687.0 250.0,735.0 250.0,735.0 250.0,783.0" data-evidence="4062-4071" />
<rect class="edge-label-bg" x="252.0" y="721.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="274.0" y="736.0">HPA</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="241.0,847.0 241.0,895.0 145.0,895.0 145.0,943.0" data-evidence="4071-4075" />
<rect class="edge-label-bg" x="140.4" y="853.0" width="105.1" height="22" rx="3" />
<text class="edge-label" x="193.0" y="868.0">Physical Page</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="259.0,847.0 259.0,895.0 355.0,895.0 355.0,943.0" data-evidence="4071-4075" />
<rect class="edge-label-bg" x="254.4" y="853.0" width="105.1" height="22" rx="3" />
<text class="edge-label" x="307.0" y="868.0">Physical Page</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="250.0,503.0 250.0,551.0 250.0,551.0 250.0,599.0" data-evidence="4053-4062" />
<rect class="edge-label-bg" x="228.2" y="537.0" width="91.7" height="22" rx="3" />
<text class="edge-label" x="274.0" y="552.0">VM Boundary</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="250.0,152.0 250.0,200.0 250.0,200.0 250.0,248.0" data-evidence="4036-4043" />
<rect class="edge-label-bg" x="252.0" y="186.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="274.0" y="201.0">GVA</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="250.0,319.0 250.0,367.0 250.0,367.0 250.0,415.0" data-evidence="4043-4048" />
<rect class="edge-label-bg" x="238.2" y="353.0" width="71.6" height="22" rx="3" />
<text class="edge-label" x="274.0" y="368.0">TLB Miss</text>
<g id="node-guest-process">
<rect class="node-shape kind-actor emphasis-normal role-source" data-evidence="4034-4038" x="159.5" y="81.0" width="181.0" height="71.0" rx="7" />
<text class="node-label" x="250.0" y="108.0">Keycloak · PostgreSQL</text>
<line class="node-detail-divider" x1="173.5" y1="129.0" x2="326.5" y2="129.0" />
<text class="node-detail" x="175.5" y="146.0">Guest Userspace</text>
</g>
<g id="node-guest-tlb">
<rect class="node-shape kind-component emphasis-normal role-store" data-evidence="4043-4045" x="175.0" y="248.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="250.0" y="275.0">Guest TLB</text>
<line class="node-detail-divider" x1="189.0" y1="296.0" x2="311.0" y2="296.0" />
<text class="node-detail" x="191.0" y="313.0">Guest Kernel</text>
</g>
<g id="node-guest-page-table">
<rect class="node-shape kind-component emphasis-normal role-service" data-evidence="4048-4053" x="175.0" y="415.0" width="150.0" height="88.0" rx="7" />
<text class="node-label" x="250.0" y="442.0">Guest Page Table</text>
<line class="node-detail-divider" x1="189.0" y1="463.0" x2="311.0" y2="463.0" />
<text class="node-detail" x="191.0" y="480.0">Guest Kernel</text>
<text class="node-detail" x="191.0" y="496.0">GVA → GPA</text>
</g>
<g id="node-ept">
<rect class="node-shape kind-component emphasis-primary role-service" data-evidence="4055-4067" x="175.0" y="599.0" width="150.0" height="88.0" rx="7" />
<text class="node-label" x="250.0" y="626.0">EPT</text>
<line class="node-detail-divider" x1="189.0" y1="647.0" x2="311.0" y2="647.0" />
<text class="node-detail" x="191.0" y="664.0">KVM / CPU</text>
<text class="node-detail" x="191.0" y="680.0">GPA → HPA</text>
</g>
<g id="node-host-physical-memory">
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="4069-4072" x="163.0" y="783.0" width="174.0" height="64.0" rx="7" />
<text class="node-label" x="250.0" y="813.0">Host Physical Memory</text>
</g>
<g id="node-numa-node-0">
<rect class="node-shape kind-store emphasis-normal role-sink" data-evidence="4073-4075" x="70.0" y="943.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="973.0">NUMA Node 0</text>
</g>
<g id="node-numa-node-1">
<rect class="node-shape kind-store emphasis-normal role-sink" data-evidence="4073-4075" x="280.0" y="943.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="355.0" y="973.0">NUMA Node 1</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB