前面的文章說到 io_uring
是 Linux 中最新的原生異步 I/O 實現,實際上 io_uring
也支持 polling,是良好的 epoll 替代品。linux
使用 io_uring
來 poll 一個 fd 很簡單。首先初始化 io_uring 對象(io_uring_queue_init),拿到 sqe(io_uring_get_sqe)是全部 io_uring
操做都必要的,前文已經介紹這裏不作過多說明。拿到 sqe 以後,使用 io_uring_prep_poll_add 初始化 sqe 指針。git
static inline void io_uring_prep_poll_add(struct io_uring_sqe *sqe, int fd, short poll_mask);
第一個參數就是前面得到的 sqe 指針;第二個參數是你要 poll 的文件描述符;第三個是標誌位,這裏 io_uring 沒有引入新的標誌(宏),而是沿用了 poll(2)
定義的標誌,如 POLLIN、POLLOUT 等。編程
如其餘 I/O 請求同樣,每一個 sqe 均可以設置一個用戶本身的值在裏面,使用 io_uring_sqe_set_data
segmentfault
能夠看到一次只能添加一個 poll 請求。若是有多個 fd,那麼重複調用 io_uring_get_sqe
獲取多個 sqe 指針分別 io_uring_prep_poll_add
便可。io_uring_get_sqe
不是系統調用不會進入內核,io_uring_prep_poll_add
則是簡單的結構體參數賦值,因此沒有速度問題。網絡
添加完須要的請求後使用 io_uring_submit
統一提交、使用 io_uring_peek_cqe
獲取完成狀況等操做與標準異步 I/O 請求一致。異步
使用 io_uring
作 polling 與 epoll、poll 的默認模式有一個很大的區別就是 io_uring
的 polling 始終工做在 one-shot 模式下(等同於 epoll 的 EPOLLONESHOT
),即一旦某個 poll 操做完成,用戶必須從新提交 poll 請求不然不會觸發新的事件,這樣保證每一個 poll 請求有且只有一個響應。而後既然是 one-shot 模式,也就沒有相似 epoll 中的 LT、ET 模式之分socket
清除進行中的 polling 請求使用 io_uring_prep_poll_remove異步編程
static inline void io_uring_prep_poll_remove(struct io_uring_sqe *sqe, void *user_data);
也是須要 sqe 而後 submit。能夠看到這個函數很特別的直接須要 user_data 參數。內核是在用以前提交的 user_data 和你如今指定的 user_data 作對比,刪除值相等的請求。函數
在網絡編程中最開始的需求就是異步監聽客戶端接入(O_NONBLOCK accept),這也是好多 epoll 的代碼示例。用 io_uring
以下:.net
int sockfd = socket(...); bind(...); listen(...); struct io_uring ring; io_uring_queue_init(32, &ring, 0); struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_poll_add(sqe, sockfd, POLLIN); io_uring_submit(&ring); struct io_uring_cqe *cqe; io_uring_wait_cqe(&ring, &cqe); int clientfd = accept(sockfd, ...);
我的感受若是拿 io_uring
純作 polling 的話沒有什麼優點。拿 io_uring
作 polling 最有用的一點是把 polling 和 aio 的完成事件作統一監聽和處理。想象拿到 clientfd 以後就能夠當即使用 io_uring_prep_readv
讀取請求體,同時又能夠再使用 io_uring_prep_poll_add
接受其餘客戶端接入,這樣纔是真正的異步編程。