在先前的設計中,我們透過 KVM 建立了虛擬的中斷控制器(PIC/IOAPIC),讓 Guest 能夠接收外部中斷。這個做法可以模擬傳統硬體的行為,但對於某些需要頻繁觸發的裝置事件(如鍵盤輸入或網卡收包),將存在效能瓶頸。
這是因為,這類事件必須先由 Host 接收,然後再透過 KVM_IRQ_LINE 把對應的 GSI 拉高,通知 Guest 有中斷發生。這條路徑會經過 kernel → host → KVM → Guest 的多層切換,每一次觸發都伴隨著 VM-exit,顯得笨重。一旦模擬裝置的事件發生得很頻繁,就會浪費大量效能在切換上。
為了解決這個問題,Linux 提供了一個更高效的機制 : eventfd。它將「事件」抽象成一個可寫入的計數器。同時,KVM 也提供了 KVM_IRQFD 介面,允許我們將一個 eventfd 綁定到指定的 IRQ 線(GSI)。
如此當事件被觸發實,只需要往 eventfd 寫入數據,就能直接在 Guest 中觸發對應的 IRQ,無須讓 VM-exit 介入整個過程。
這裡要向我們西前設計的 irq_manager 增加兩件事情
int irq_bind_eventfd(struct irq_manager *mgr, unsigned gsi, int efd)
{
struct kvm_irqfd args = {
.fd = efd,
.gsi = gsi,
.flags = 0,
};
return ioctl(mgr->vmfd, KVM_IRQFD, &args);
}
int irq_trigger(int efd)
{
uint64_t one = 1;
return write(efd, &one, sizeof(one));
}
irq_bind_eventfd 用於將 eventfd 綁定到 GSI,綁定完成後只需要透過 write 即可觸發 IRQ,另外這個 one = 1 實際上代表觸發次數,也就是最小的寫入事件,調用 write 實等於告訴 eventfd 目前事件累計多少次。