非同步與高效能輸入輸出

IOCP 與 POSIX AIO

/ IOCP -> EYE-oh-see-pee; AIO -> AY-eye-oh /

早在 io_uring 之前,另有兩套系統提供完成模型的 I/O——「替我做傳輸,做完了告訴我」。在 Windows 上那是 IOCP,即 I/O 完成連接埠,是每一個正經 Windows 伺服器的骨幹。在 Unix 上,POSIX 標準定義了 POSIX AIO,一個紙面上看似非同步、實務上卻弱得多的介面。兩者都與 io_uring 同屬完成模型家族。

IOCP 的運作像個有小班人馬的共享卸貨碼頭。你把通訊端與檔案控制代碼關聯到一個完成連接埠,然後啟動重疊(非同步)操作如 WSARecv 或 ReadFile,它們立刻返回,而作業系統在背景做實際傳輸。一個小而固定的工作執行緒池呼叫 GetQueuedCompletionStatus(),它阻塞到有完成的操作被張貼到連接埠,然後把完成的結果交給那個執行緒。核心本身會平衡有多少執行緒可運行,所以寥寥幾個執行緒就有效率地服務數萬條連線——這是 Windows 對 C10k 的原生答案。POSIX AIO 提供 aio_read() 與 aio_write():你提交一個 struct aiocb,之後以信號或回呼被通知。但在 Linux 上它歷來是在使用者空間用輔助執行緒實作、而非在核心裡,主要只涵蓋一般檔案,並被普遍視為令人失望——這正是 io_uring 之所以被創造的一大原因。

為何重要:知道這些能讓完成模型的版圖更完整。反應器與前攝器之分直接對應到平台——反應器(就緒)配 epoll 與 kqueue,前攝器(完成)配 IOCP 與 io_uring。可攜的非同步函式庫(libuv、ASIO、Tokio)把這一切藏起來,在 Linux 的 epoll、macOS 的 kqueue、Windows 的 IOCP 之上呈現單一介面,所以多數應用程式碼從不直接呼叫這些 API。

/* Windows IOCP,示意 */ HANDLE port = CreateIoCompletionPort(sock, NULL, key, 0); WSARecv(sock, &wsabuf, 1, NULL, &flags, &overlapped, NULL); /* 工作執行緒: */ GetQueuedCompletionStatus(port, &bytes, &key, &ov, INFINITE);

在 Windows 上你啟動一個重疊的 recv,之後由工作執行緒從連接埠收取完成通知。

POSIX AIO 不等於 io_uring,也不是現代 Linux 非同步 I/O 的所在——在 Linux 上它通常是個限於檔案的使用者空間執行緒池墊片,所以別拿它來做可擴展的網路。相對地,IOCP 是真正核心層級的,是高效能 Windows I/O 的真實模型。

又稱
I/O completion portsWindows completion-based I/OI/O 完成連接埠(IOCP)