{ "version": "1.1", "id": "guest-block-io-to-virtqueue", "title": "게스트의 write() 가 virtqueue 에 실리기까지", "question": "게스트 애플리케이션의 write() 는 virtqueue 에 실리기 전에 게스트의 어느 계층을 어떤 순서로 지나는가", "type": "flow", "direction": "TB", "audience": [ "게스트에서 본 저장소 지연을 계층별로 가르려는 사람", "virtio-blk 경로를 처음 읽는 사람" ], "summary": "게스트 애플리케이션의 write() 는 VFS 와 ext4/XFS, Guest Page Cache, Guest Block Layer, /dev/vda, virtio-blk Frontend 를 차례로 지나 virtqueue 에 실린다.", "alt": "게스트 사용자 공간의 PostgreSQL/Keycloak 에서 시작한 write() 가 게스트 커널의 VFS, ext4/XFS, Guest Page Cache, Guest Block Layer, /dev/vda, virtio-blk Frontend 를 거쳐 virtqueue 에 도달하는 위에서 아래로 읽는 흐름도.", "long_description": "위에서 아래로 읽는다. 맨 위 Guest Userspace 구역에 PostgreSQL / Keycloak 이 있고 write() 가 게스트 커널로 내려간다. Guest Kernel 구역 안에서 VFS 가 먼저 받고, ext4 / XFS 가 파일을 block 공간에 배치하고, Guest Page Cache 가 그 내용을 dirty page 로 받아 두었다가 writeback 으로 Guest Block Layer 에 내려보낸다. Guest Block Layer 는 그것을 WRITE 나 FLUSH 요청으로 바꿔 /dev/vda 에 걸고, virtio-blk Frontend 가 그 요청을 virtqueue 에 게시한다. virtqueue 아래의 VM Boundary 부터는 QEMU 와 호스트 계층이라 이 그림에서 잘랐다. 완료가 같은 virtqueue 로 되돌아오는 역방향은 SSOT 의 다른 절이 대는 사실이라 이 그림에 넣지 않았고 본문 문단이 맡는다.", "source_context": { "document": "docs/virtualization/final/document.md", "document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd", "anchor": { "kind": "heading", "value": "128. 전체 구조", "line": 5804 } }, "composition": { "profile": "component-flow", "diagram_only": true, "reference_ids": [ "payment-event-flow" ], "rationale": "이 절이 주장하는 것은 요청 하나가 한 방향으로 지나가는 계층의 순서다. sequence 는 참여자마다 생명선을 세우고 메시지를 주고받는 그림인데 이 구간에는 되돌아오는 메시지가 없어 생명선이 전부 빈 세로줄이 된다. two-zone-pipeline 은 구역 안쪽 엣지 라벨을 캔버스 위쪽에 고정해 writeback 과 WRITE / FLUSH 가 제자리를 잃는다. 구역은 groups 로 두고 흐름은 component-flow 로 세운다.", "focus_node": "virtqueue" }, "groups": [ { "id": "guest-userspace", "label": "Guest Userspace", "kind": "boundary", "description": "게스트 사용자 공간. 저장장치를 직접 다루지 않는다.", "evidence": [ { "start_line": 5807, "end_line": 5809 } ], "assumption": false }, { "id": "guest-kernel", "label": "Guest Kernel", "kind": "boundary", "description": "게스트 커널 공간. VM Boundary 위쪽 계층이 전부 여기 있다.", "evidence": [ { "start_line": 5815, "end_line": 5833 } ], "assumption": false } ], "nodes": [ { "id": "app", "label": "PostgreSQL / Keycloak", "kind": "service", "role": "source", "shape": "box", "group": "guest-userspace", "description": "파일 연산을 요청하는 게스트 프로세스.", "evidence": [ { "start_line": 5807, "end_line": 5809 }, { "start_line": 5885, "end_line": 5885 }, { "start_line": 5811, "end_line": 5813 } ], "assumption": false, "details": [ "read / write", "fsync / sync" ] }, { "id": "vfs", "label": "VFS", "kind": "service", "role": "service", "shape": "box", "group": "guest-kernel", "description": "열린 파일이 어느 filesystem 구현에 속하는지 보고 연산을 넘기는 공통 계층.", "evidence": [ { "start_line": 5815, "end_line": 5817 }, { "start_line": 5905, "end_line": 5905 } ], "assumption": false }, { "id": "fs", "label": "ext4 / XFS", "kind": "service", "role": "service", "shape": "box", "group": "guest-kernel", "description": "파일과 디렉터리를 block 공간에 배치하는 게스트 filesystem.", "evidence": [ { "start_line": 5819, "end_line": 5819 } ], "assumption": false }, { "id": "page-cache", "label": "Guest Page Cache", "kind": "store", "role": "store", "shape": "box", "group": "guest-kernel", "description": "게스트 RAM 에 있는 cache. write() 가 성공해도 데이터는 아직 여기까지만 확정된다.", "evidence": [ { "start_line": 5821, "end_line": 5821 } ], "assumption": false }, { "id": "block-layer", "label": "Guest Block Layer", "kind": "service", "role": "service", "shape": "box", "group": "guest-kernel", "description": "파일 세계의 요청을 block device 세계의 요청으로 바꾸는 계층.", "evidence": [ { "start_line": 5825, "end_line": 5825 } ], "assumption": false }, { "id": "vda", "label": "/dev/vda", "kind": "device", "role": "service", "shape": "box", "group": "guest-kernel", "description": "게스트 Linux 가 하나의 block device 로 인식하는 이름.", "evidence": [ { "start_line": 5829, "end_line": 5829 }, { "start_line": 5879, "end_line": 5879 } ], "assumption": false }, { "id": "virtio-blk", "label": "virtio-blk Frontend", "kind": "service", "role": "service", "shape": "box", "group": "guest-kernel", "description": "그 가상 block device 를 제어하는 게스트 커널 driver.", "evidence": [ { "start_line": 5831, "end_line": 5831 }, { "start_line": 5789, "end_line": 5789 } ], "assumption": false }, { "id": "virtqueue", "label": "virtqueue", "kind": "queue", "role": "queue", "shape": "box", "emphasis": "primary", "group": "guest-kernel", "description": "요청이 실리는 자리. 바로 아래가 VM Boundary 다.", "evidence": [ { "start_line": 5833, "end_line": 5835 } ], "assumption": false } ], "edges": [ { "id": "app-to-vfs", "from": "app", "to": "vfs", "label": "", "kind": "data", "style": "solid", "order": 1, "evidence": [ { "start_line": 5811, "end_line": 5813 }, { "start_line": 5890, "end_line": 5890 } ], "assumption": false }, { "id": "vfs-to-fs", "from": "vfs", "to": "fs", "label": "", "kind": "data", "style": "solid", "order": 2, "evidence": [ { "start_line": 5817, "end_line": 5819 } ], "assumption": false }, { "id": "fs-to-page-cache", "from": "fs", "to": "page-cache", "label": "", "kind": "data", "style": "solid", "order": 3, "evidence": [ { "start_line": 5819, "end_line": 5821 } ], "assumption": false }, { "id": "page-cache-to-block-layer", "from": "page-cache", "to": "block-layer", "label": "writeback", "kind": "data", "style": "solid", "order": 4, "evidence": [ { "start_line": 5823, "end_line": 5825 } ], "assumption": false }, { "id": "block-layer-to-vda", "from": "block-layer", "to": "vda", "label": "WRITE / FLUSH", "kind": "data", "style": "solid", "order": 5, "evidence": [ { "start_line": 5827, "end_line": 5829 } ], "assumption": false }, { "id": "vda-to-virtio-blk", "from": "vda", "to": "virtio-blk", "label": "", "kind": "data", "style": "solid", "order": 6, "evidence": [ { "start_line": 5829, "end_line": 5831 } ], "assumption": false }, { "id": "virtio-blk-to-virtqueue", "from": "virtio-blk", "to": "virtqueue", "label": "Virtio block request", "kind": "data", "style": "solid", "order": 7, "evidence": [ { "start_line": 5831, "end_line": 5833 } ], "assumption": false } ], "legend": [], "metadata": { "rationale": "component-flow 를 고른 이유는 이 절이 답하는 물음이 「write() 가 virtqueue 에 닿기까지 무엇을 어떤 순서로 지나는가」이기 때문이다. Guest Userspace 와 Guest Kernel 은 §128 이 대괄호로 직접 그어 둔 구역이라 groups 로 두었다. 완료가 같은 virtqueue 로 되돌아오는 역방향은 §166 과 §172 가 대는 사실인데 두 절 모두 이 앵커의 준비된 문맥(§127~§129) 밖이라 넣지 않았다 — 그 문맥 안에서 인용할 줄이 없다. VM Boundary 아래의 QEMU·호스트 계층은 같은 §128 안에 있지만 이 기록이 virtqueue 까지만 따라간다고 밝혀 두었고, 그 아래는 다른 기록의 그림이 맡는다. 첫 엣지에 write() 라벨을 달았더니 라벨 상자가 Guest Kernel 구역 이름을 덮어(PNG 로 확인) 라벨을 지우고 그 두 줄을 출발 노드의 details 로 내렸다. virtio-blk Frontend → virtqueue 엣지의 라벨 「Virtio block request」는 이 앵커의 문맥이 화살표만 그려 두어 이름 자체는 §138 이 대는 것이고, 기록 본문도 같은 이름을 쓴다." } }