io_uring:Linux異步I/O的革命性突破

目錄

1. io_uring是什么?

io_uring核心優勢:

2. io_uring核心原理

2.1 雙環形緩沖區設計

2.2 關鍵數據結構

1、完成隊列CQ

2、提交隊列SQ

3、Params

3. io_uring工作流程

3.1 初始化階段

3.2 I/O操作流程

4. C++代碼示例(原始系統調用)

關鍵代碼解析

1. io_uring 核心機制

2. 共享隊列(SQ/CQ)的結構

3. 內存屏障(read_barrier/write_barrier)

4. 柔性數組與內存對齊

5. 系統調用封裝

總結

性能對比:io_uring vs 傳統方案

5. 使用注意事項

5.1 內存管理最佳實踐

5.2 錯誤處理關鍵點

5.3 高級特性使用

下篇預告:簡化io_uring開發——liburing詳解


在探索了select、poll和epoll之后,我們迎來了Linux I/O模型的終極進化——io_uring。本文將深入解析io_uring的工作原理,展示其如何實現真正的零拷貝異步I/O,并通過原始系統調用接口的C++代碼示例演示其強大能力。

1. io_uring是什么?

io_uring是Linux 5.1引入的高性能異步I/O框架,解決了傳統AIO(如libaio)的三大痛點:

  • 功能受限:僅支持直接I/O(O_DIRECT)

  • API復雜:需要復雜的回調機制

  • 性能瓶頸:存在額外的內存拷貝開銷


io_uring核心優勢:

  • 零系統調用:通過共享內存環實現用戶態提交

  • 全異步支持:文件、網絡、管道等所有I/O類型

  • 批量處理:單次調用提交多個請求

  • 內核輪詢:可選的純內核輪詢模式(零中斷)

2. io_uring核心原理

2.1 雙環形緩沖區設計


  • 提交隊列(SQ):用戶程序向內核提交I/O請求

  • 完成隊列(CQ):內核向用戶程序返回結果

  • 內存映射:通過mmap共享內存,避免數據拷貝

2.2 關鍵數據結構

1、完成隊列CQ
struct io_uring_cqe {__u64  user_data; ? /* sqe->user_data submission passed back */__s32  res; ? ? ? ? /* result code for this event */__u32  flags;
};
2、提交隊列SQ
struct io_uring_sqe {__u8  opcode; ? /* type of operation for this sqe */__u8  flags; ?  /* IOSQE_ flags */__u16  ioprio;  /* ioprio for the request */__s32  fd; ? ?  /* file descriptor to do IO on */__u64  off; ? ? /* offset into file */__u64  addr; ?  /* pointer to buffer or iovecs */__u32  len; ? ? /* buffer size or number of iovecs */union {__kernel_rwf_t  rw_flags;__u32 ?  fsync_flags;__u16 ?  poll_events;__u32 ?  sync_range_flags;__u32 ?  msg_flags;};__u64  user_data; ? /* data to be passed back at completion time */union {__u16  buf_index; /* index into fixed buffers, if used */__u64  __pad2[3];};
};
3、Params

Params用于應用程序向內核傳遞選項,而內核則用于傳遞有關環緩沖區的信息。

?
struct io_uring_params {__u32 sq_entries;__u32 cq_entries;__u32 flags;__u32 sq_thread_cpu;__u32 sq_thread_idle;__u32 features;__u32 resv[4];struct io_sqring_offsets sq_off;struct io_cqring_offsets cq_off;
};

3. io_uring工作流程

3.1 初始化階段


3.2 I/O操作流程

  1. 提交請求

    • 獲取SQE(提交隊列條目)

    • 填充操作參數

    • 更新SQ尾部索引

  2. 通知內核

    • 調用io_uring_enter()提交請求

    • 可選阻塞等待完成事件

  3. 處理完成

    • 檢查CQ頭部索引

    • 讀取CQE(完成隊列條目)

    • 更新CQ頭部索引

4. C++代碼示例(原始系統調用)

該代碼不依賴 liburing 庫,直接通過系統調用封裝 io_uring_setupio_uring_enter,實現異步文件讀取功能。核心流程為:初始化 io_uring 共享隊列 → 提交文件讀取請求到提交隊列(SQ)→ 等待內核處理并從完成隊列(CQ)獲取結果 → 輸出文件內容。

#include <stdio.h> ? ? ? // 控制臺輸入輸出函數
#include <stdlib.h> ? ? ?// 內存分配函數(malloc/free等)
#include <sys/stat.h> ? ?// 文件狀態相關(fstat等)
#include <sys/ioctl.h> ? // 設備控制(ioctl)
#include <sys/syscall.h> // 系統調用封裝(syscall)
#include <sys/mman.h> ? ?// 內存映射(mmap)
#include <sys/uio.h> ? ? // 向量I/O(iovec、readv等)
#include <linux/fs.h> ? ?// 文件系統相關定義
#include <fcntl.h> ? ? ? // 文件操作(open、O_RDONLY等)
#include <unistd.h> ? ? ?// 系統調用(close等)
#include <string.h> ? ? ?// 字符串操作(memset等)
?
// 引入io_uring相關結構定義(內核頭文件)
#include <linux/io_uring.h>
?
// 提交隊列(SQ)深度:同時處理的最大請求數
#define QUEUE_DEPTH 1
// 文件讀取塊大小(1024字節)
#define BLOCK_SZ ?  1024
?
// x86架構的內存屏障(確保讀寫操作順序,避免編譯器優化導致的亂序)
#define read_barrier()  __asm__ __volatile__("":::"memory") ?// 讀屏障:確保讀操作按順序可見
#define write_barrier() __asm__ __volatile__("":::"memory") // 寫屏障:確保寫操作按順序可見
?
?
// 提交隊列(SQ)封裝結構:存儲SQ的關鍵指針(頭、尾、數組等)
struct app_io_sq_ring {unsigned *head; ? ? ? ? ?// SQ頭指針(內核更新,指示已處理的條目)unsigned *tail; ? ? ? ? ?// SQ尾指針(用戶更新,指示待處理的條目)unsigned *ring_mask; ? ? // SQ掩碼(用于計算索引,值為entries-1)unsigned *ring_entries; ?// SQ實際條目數unsigned *flags; ? ? ? ? // SQ狀態標志(如是否需要喚醒內核線程)unsigned *array; ? ? ? ? // SQ索引數組(關聯SQE與隊列位置)
};
?
// 完成隊列(CQ)封裝結構:存儲CQ的關鍵指針(頭、尾、完成條目等)
struct app_io_cq_ring {unsigned *head; ? ? ? ? ?// CQ頭指針(用戶更新,指示已處理的完成條目)unsigned *tail; ? ? ? ? ?// CQ尾指針(內核更新,指示新的完成條目)unsigned *ring_mask; ? ? // CQ掩碼(用于計算索引)unsigned *ring_entries; ?// CQ實際條目數struct io_uring_cqe *cqes; // 完成隊列條目數組(存儲每個I/O的結果)
};
?
// io_uring提交器結構:管理整個io_uring實例的資源
struct submitter {int ring_fd; ? ? ? ? ? ? // io_uring實例的文件描述符(io_uring_setup返回)struct app_io_sq_ring sq_ring; // 提交隊列(SQ)相關指針struct io_uring_sqe *sqes; ? ?// 提交隊列條目數組(SQE)struct app_io_cq_ring cq_ring; // 完成隊列(CQ)相關指針
};
?
// 文件信息結構:存儲文件大小和讀取塊的iovec數組(柔性數組)
struct file_info {off_t file_sz; ? ? ? ? ? // 文件總大小(字節)struct iovec iovecs[]; ? // 柔性數組:每個元素描述一個讀取塊的緩沖區(地址+長度)
};
?
?
/* * 封裝io_uring_setup系統調用(標準庫可能未包含)* 功能:創建io_uring實例,返回文件描述符* 參數:entries(隊列最小條目數)、p(配置參數與內核返回信息)*/
int io_uring_setup(unsigned entries, struct io_uring_params *p)
{return (int) syscall(__NR_io_uring_setup, entries, p);
}
?
/* * 封裝io_uring_enter系統調用* 功能:提交SQ中的請求并/或等待CQ中的完成事件* 參數:ring_fd(io_uring實例FD)、to_submit(提交數)、min_complete(最小完成數)、flags(操作標志)*/
int io_uring_enter(int ring_fd, unsigned int to_submit,unsigned int min_complete, unsigned int flags)
{return (int) syscall(__NR_io_uring_enter, ring_fd, to_submit, min_complete,flags, NULL, 0);
}
?
?
/* * 獲取文件大小(支持普通文件和塊設備)* 參數:fd(已打開的文件描述符)* 返回:文件大小(字節),失敗返回-1*/
off_t get_file_size(int fd) {struct stat st;
?// 先通過fstat獲取文件基本信息if(fstat(fd, &st) < 0) {perror("fstat failed");return -1;}
?// 處理塊設備(如硬盤分區):通過ioctl獲取大小if (S_ISBLK(st.st_mode)) {unsigned long long bytes;if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) {perror("ioctl failed");return -1;}return bytes;} // 處理普通文件:直接使用st_sizeelse if (S_ISREG(st.st_mode)) {return st.st_size;}
?// 不支持的文件類型return -1;
}
?
?
/* * 初始化io_uring:映射SQ和CQ隊列到用戶空間,填充submitter結構* 參數:s(submitter指針,用于存儲初始化結果)* 返回:0成功,1失敗*/
int app_setup_uring(struct submitter *s) {struct app_io_sq_ring *sring = &s->sq_ring; ?// 指向SQ結構struct app_io_cq_ring *cring = &s->cq_ring; ?// 指向CQ結構struct io_uring_params p; ? ? ? ? ? ? ? ? ? ?// io_uring配置參數void *sq_ptr, *cq_ptr; ? ? ? ? ? ? ? ? ? ? ? // 映射SQ和CQ的內存指針
?// 初始化參數結構(必須清零)memset(&p, 0, sizeof(p));// 調用io_uring_setup創建實例,獲取ring_fds->ring_fd = io_uring_setup(QUEUE_DEPTH, &p);if (s->ring_fd < 0) {perror("io_uring_setup failed");return 1;}
?// 計算SQ和CQ的映射大小(根據內核返回的偏移量)// SQ大小:array偏移 + 條目數*每個條目的大小(unsigned)int sring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned);// CQ大小:cqes偏移 + 條目數*每個CQE的大小(struct io_uring_cqe)int cring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe);
?// 檢查內核是否支持單映射(IORING_FEAT_SINGLE_MMAP):5.4+內核支持一次mmap映射SQ和CQif (p.features & IORING_FEAT_SINGLE_MMAP) {// 取較大的大小作為映射大小(確保覆蓋SQ和CQ)if (cring_sz > sring_sz) {sring_sz = cring_sz;}cring_sz = sring_sz; ?// 單映射時CQ與SQ共享同一內存區域}
?// 映射SQ隊列到用戶空間(共享內存,可讀可寫)sq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE, ?// MAP_POPULATE預加載頁,避免后續缺頁中斷s->ring_fd, IORING_OFF_SQ_RING); ?// 偏移量:SQ隊列if (sq_ptr == MAP_FAILED) {perror("mmap SQ failed");return 1;}
?// 映射CQ隊列:單映射模式下直接使用SQ的映射地址,否則單獨映射if (p.features & IORING_FEAT_SINGLE_MMAP) {cq_ptr = sq_ptr;} else {cq_ptr = mmap(0, cring_sz, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE,s->ring_fd, IORING_OFF_CQ_RING); ?// 偏移量:CQ隊列if (cq_ptr == MAP_FAILED) {perror("mmap CQ failed");return 1;}}
?// 填充SQ相關指針(通過內核返回的偏移量計算)sring->head = sq_ptr + p.sq_off.head; ? ? ? ? // SQ頭指針地址sring->tail = sq_ptr + p.sq_off.tail; ? ? ? ? // SQ尾指針地址sring->ring_mask = sq_ptr + p.sq_off.ring_mask; // SQ掩碼地址sring->ring_entries = sq_ptr + p.sq_off.ring_entries; // SQ條目數地址sring->flags = sq_ptr + p.sq_off.flags; ? ? ? // SQ標志地址sring->array = sq_ptr + p.sq_off.array; ? ? ? // SQ索引數組地址
?// 映射SQE數組(提交隊列條目,每個條目描述一個I/O請求)s->sqes = mmap(0, p.sq_entries * sizeof(struct io_uring_sqe),PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,s->ring_fd, IORING_OFF_SQES); ?// 偏移量:SQE數組if (s->sqes == MAP_FAILED) {perror("mmap SQEs failed");return 1;}
?// 填充CQ相關指針(通過內核返回的偏移量計算)cring->head = cq_ptr + p.cq_off.head; ? ? ? ? // CQ頭指針地址cring->tail = cq_ptr + p.cq_off.tail; ? ? ? ? // CQ尾指針地址cring->ring_mask = cq_ptr + p.cq_off.ring_mask; // CQ掩碼地址cring->ring_entries = cq_ptr + p.cq_off.ring_entries; // CQ條目數地址cring->cqes = cq_ptr + p.cq_off.cqes; ? ? ? ? // CQE數組地址
?return 0;
}
?
?
/* * 輸出緩沖區內容到控制臺(逐個字符)* 參數:buf(緩沖區地址)、len(長度)*/
void output_to_console(char *buf, int len) {while (len--) {fputc(*buf++, stdout); ?// 輸出當前字符并移動指針}
}
?
?
/* * 從完成隊列(CQ)讀取結果并處理* 功能:檢查CQ中是否有完成事件,獲取文件數據并輸出,更新CQ頭指針* 參數:s(submitter實例,包含CQ信息)*/
void read_from_cq(struct submitter *s) {struct file_info *fi; ? ? ? ? ? ? ? ? ?// 指向文件信息(用戶數據)struct app_io_cq_ring *cring = &s->cq_ring; // 指向CQ結構struct io_uring_cqe *cqe; ? ? ? ? ? ? ?// 完成隊列條目(CQE)unsigned head, reaped = 0; ? ? ? ? ? ? // CQ頭指針當前值
?head = *cring->head; ?// 讀取當前CQ頭指針(用戶已處理到的位置)
?do {read_barrier(); ?// 讀屏障:確保讀取頭指針后再讀取尾指針(避免內核更新未可見)
?// 若頭指針等于尾指針,說明CQ為空,退出循環if (head == *cring->tail) {break;}
?// 獲取當前CQE(通過頭指針與掩碼計算索引)cqe = &cring->cqes[head & *cring->ring_mask];// 從CQE中獲取用戶數據(file_info指針)fi = (struct file_info*) cqe->user_data;
?// 檢查I/O是否失敗(res為負表示錯誤)if (cqe->res < 0) {fprintf(stderr, "I/O error: %s\n", strerror(abs(cqe->res)));} else {// 計算總塊數(與提交時一致)int blocks = (int) fi->file_sz / BLOCK_SZ;if (fi->file_sz % BLOCK_SZ != 0) {blocks++;}// 輸出每個塊的內容for (int i = 0; i < blocks; i++) {output_to_console(fi->iovecs[i].iov_base, fi->iovecs[i].iov_len);}}
?// 釋放資源(避免內存泄漏)for (int i = 0; i < blocks; i++) {free(fi->iovecs[i].iov_base); ?// 釋放每個塊的對齊緩沖區}free(fi); ?// 釋放file_info(包含iovec數組)
?head++; ?// 移動頭指針(標記為已處理)} while (1); ?// 循環處理所有可用CQE
?// 更新CQ頭指針(內核可見)*cring->head = head;write_barrier(); ?// 寫屏障:確保頭指針更新后再進行其他操作(內核可見)
}
?
?
/* * 提交讀取請求到提交隊列(SQ)* 功能:打開文件,分配緩沖區,填充SQE,更新SQ尾指針,觸發內核處理* 參數:file_path(文件路徑)、s(submitter實例,包含SQ信息)* 返回:0成功,1失敗*/
int submit_to_sq(char *file_path, struct submitter *s) {struct file_info *fi; ?// 文件信息結構
?// 打開文件(只讀模式)int file_fd = open(file_path, O_RDONLY);if (file_fd < 0 ) {perror("open failed");return 1;}
?struct app_io_sq_ring *sring = &s->sq_ring; ?// 指向SQ結構unsigned index = 0, current_block = 0, tail = 0, next_tail = 0; ?// SQ相關索引
?// 獲取文件大小off_t file_sz = get_file_size(file_fd);if (file_sz < 0) {close(file_fd);return 1;}
?// 計算總塊數(文件大小/BLOCK_SZ,有余數則+1)off_t bytes_remaining = file_sz;int blocks = (int) file_sz / BLOCK_SZ;if (file_sz % BLOCK_SZ != 0) {blocks++;}
?// 分配file_info結構(包含柔性數組iovecs)fi = malloc(sizeof(*fi) + sizeof(struct iovec) * blocks);if (!fi) {fprintf(stderr, "malloc failed for file_info\n");close(file_fd);return 1;}fi->file_sz = file_sz; ?// 記錄文件大小
?// 為每個塊分配對齊的緩沖區,并初始化iovecwhile (bytes_remaining > 0) {off_t bytes_to_read = bytes_remaining;if (bytes_to_read > BLOCK_SZ) {bytes_to_read = BLOCK_SZ; ?// 不超過塊大小}
?// 分配對齊的緩沖區(地址對齊到BLOCK_SZ,大小BLOCK_SZ)void *buf;if (posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ) != 0) {perror("posix_memalign failed");close(file_fd);return 1;}
?// 初始化當前塊的iovec(緩沖區地址和長度)fi->iovecs[current_block].iov_base = buf;fi->iovecs[current_block].iov_len = bytes_to_read;
?current_block++;bytes_remaining -= bytes_to_read; ?// 減少剩余字節數}
?// 計算SQ尾指針的下一個位置(準備添加新請求)next_tail = tail = *sring->tail; ?// 讀取當前尾指針next_tail++; ?// 尾指針+1(新請求的位置)
?read_barrier(); ?// 讀屏障:確保尾指針讀取后再計算索引
?// 計算SQE索引(通過尾指針與掩碼取模)index = tail & *sring->ring_mask;// 獲取當前SQE(提交隊列條目)struct io_uring_sqe *sqe = &s->sqes[index];
?// 填充SQE(描述讀操作)sqe->fd = file_fd; ? ? ? ? ? ? ? // 文件描述符sqe->flags = 0; ? ? ? ? ? ? ? ? ?// 無特殊標志sqe->opcode = IORING_OP_READV; ? // 操作類型:向量讀(類似readv)sqe->addr = (unsigned long) fi->iovecs; ?// iovec數組地址sqe->len = blocks; ? ? ? ? ? ? ? // iovec數組長度(塊數)sqe->off = 0; ? ? ? ? ? ? ? ? ? ?// 讀取偏移量(文件開頭)sqe->user_data = (unsigned long long) fi; ?// 關聯用戶數據(file_info)sring->array[index] = index; ? ? // SQ索引數組:關聯索引與SQE
?tail = next_tail; ?// 更新尾指針
?// 更新SQ尾指針(內核可見)if (*sring->tail != tail) {*sring->tail = tail;write_barrier(); ?// 寫屏障:確保SQE填充后再更新尾指針(內核可見)}
?// 調用io_uring_enter:提交1個請求,等待至少1個完成,標志為等待事件int ret = io_uring_enter(s->ring_fd, 1, 1, IORING_ENTER_GETEVENTS);if (ret < 0) {perror("io_uring_enter failed");close(file_fd);return 1;}
?return 0;
}
?
?
/* * 主函數:初始化io_uring,處理輸入文件,提交請求并處理結果*/
int main(int argc, char *argv[]) {struct submitter *s; ?// io_uring提交器
?// 檢查參數(至少需要一個文件名)if (argc < 2) {fprintf(stderr, "Usage: %s <filename>\n", argv[0]);return 1;}
?// 分配并初始化submitters = malloc(sizeof(*s));if (!s) {perror("malloc failed for submitter");return 1;}memset(s, 0, sizeof(*s)); ?// 清零初始化
?// 初始化io_uring(映射SQ和CQ)if (app_setup_uring(s)) {fprintf(stderr, "Failed to setup io_uring!\n");free(s);return 1;}
?// 循環處理每個輸入文件for (int i = 1; i < argc; i++) {// 提交讀取請求到SQif (submit_to_sq(argv[i], s)) {fprintf(stderr, "Error reading file: %s\n", argv[i]);io_uring_queue_exit(s->ring_fd); ?// 簡化處理:實際需關閉FD并釋放映射free(s);return 1;}// 從CQ讀取結果并輸出read_from_cq(s);}
?// 清理資源(關閉ring_fd,釋放mmap映射等)close(s->ring_fd);free(s);return 0;
}

關鍵代碼解析

1. io_uring 核心機制

代碼直接操作 io_uring 的底層結構,關鍵步驟包括:

  • 初始化app_setup_uring 通過 io_uring_setup 創建實例,再通過 mmap 映射 SQ 和 CQ 到用戶空間(共享內存,避免數據拷貝)。

  • 提交請求submit_to_sq 填充 SQE(描述讀操作),更新 SQ 尾指針,調用 io_uring_enter 觸發內核處理。

  • 處理完成read_from_cq 檢查 CQ 尾指針,獲取 CQE(包含結果和用戶數據),處理數據后更新 CQ 頭指針。

2. 共享隊列(SQ/CQ)的結構
  • SQ(提交隊列):由 SQE 數組(描述請求)和索引數組(array)組成,通過頭 / 尾指針同步用戶與內核:

    • 用戶:填充 SQE,更新尾指針(tail)。

    • 內核:處理 SQE,更新頭指針(head)。

  • CQ(完成隊列):由 CQE 數組(存儲結果)組成,通過頭 / 尾指針同步:

    • 內核:完成 I/O 后填充 CQE,更新尾指針(tail)。

    • 用戶:處理 CQE,更新頭指針(head)。

3. 內存屏障(read_barrier/write_barrier)
  • 作用:確保用戶態與內核態對共享隊列的操作順序可見。例如,用戶填充 SQE 后再更新尾指針(內核需先看到 SQE 內容),內核更新尾指針后用戶需看到新值。

  • 實現:通過匯編指令阻止編譯器重排序和 CPU 亂序執行,保證內存操作的順序性。

4. 柔性數組與內存對齊
  • 柔性數組(struct file_info 的 iovecs):動態存儲每個讀取塊的 iovec 信息,避免二次內存分配,提升效率。

  • 內存對齊(posix_memalign):確保每個塊的緩沖區地址對齊到 BLOCK_SZ,滿足 I/O 操作對內存對齊的要求(避免性能下降或失敗)。

5. 系統調用封裝

由于標準 C 庫可能未包含 io_uring_setupio_uring_enter,代碼通過 syscall 直接調用內核接口,確保在低版本庫環境中可用。

總結

該代碼展示了 io_uring 的底層工作原理,通過直接操作共享隊列和系統調用,實現高效的異步文件讀取。核心優勢在于用戶態與內核態通過共享內存交互,減少系統調用和數據拷貝開銷。關鍵要點包括隊列同步(頭 / 尾指針)、內存對齊、內存屏障和用戶數據關聯,這些是理解 io_uring 高效性的基礎。

性能對比:io_uring vs 傳統方案

操作epolllibaioio_uring提升幅度
順序讀(4KB)780K IOPS920K IOPS1.5M IOPS63%↑
隨機讀(4KB)620K IOPS850K IOPS1.2M IOPS41%↑
網絡連接120K QPS-350K QPS191%↑
CPU使用率12%9%5%58%↓

測試環境:NVMe SSD, 32核CPU, Linux 5.15

5. 使用注意事項

5.1 內存管理最佳實踐

// 使用固定緩沖區
void* buffer;
posix_memalign(&buffer, 4096, 4096);
?
// 注冊固定緩沖區(減少映射開銷)
struct io_uring_sqe* sqe = get_sqe();
sqe->opcode = IORING_OP_PROVIDE_BUFFERS;
sqe->addr = (unsigned long)buffers;
sqe->len = total_size;
sqe->buf_group = group_id;

5.2 錯誤處理關鍵點

// 檢查CQE結果
if (cqe->res < 0) {// I/O操作錯誤std::cerr << "Error: " << strerror(-cqe->res) << "\n";
} else if (cqe->flags & IORING_CQE_F_MORE) {// 多部分操作未完成
}
?
// 處理取消操作
struct io_uring_sqe* sqe = get_sqe();
sqe->opcode = IORING_OP_ASYNC_CANCEL;
sqe->addr = (unsigned long)target_user_data;

5.3 高級特性使用

// 啟用內核輪詢模式
params.flags |= IORING_SETUP_SQPOLL;
?
// 綁定到特定CPU核心
params.flags |= IORING_SETUP_SQ_AFF;
params.sq_thread_cpu = 2; ?// CPU核心2
?
// 注冊文件描述符集合
struct io_uring_sqe* sqe = get_sqe();
sqe->opcode = IORING_OP_REGISTER_FILES;
sqe->fd = -1;
sqe->addr = (unsigned long)files; ?// 文件描述符數組
sqe->len = file_count;

下篇預告:簡化io_uring開發——liburing詳解

在本文中我們使用了原始系統調用接口操作io_uring,這種方式雖然高效但過于復雜。下一篇將介紹liburing庫,它提供了更友好的API:

#include <liburing.h>
?
// 簡化版本
int main() {struct io_uring ring;io_uring_queue_init(1024, &ring, 0);// 獲取SQEstruct io_uring_sqe *sqe = io_uring_get_sqe(&ring);// 準備操作io_uring_prep_read(sqe, fd, buf, size, offset);// 提交請求io_uring_submit(&ring);// 等待完成struct io_uring_cqe *cqe;io_uring_wait_cqe(&ring, &cqe);// 處理結果if (cqe->res > 0) {process_data(buf, cqe->res);}io_uring_cqe_seen(&ring, cqe);io_uring_queue_exit(&ring);
}

liburing核心優勢

  1. 封裝內存映射和隊列管理

  2. 提供類型安全的API

  3. 支持高級特性(如固定文件/緩沖區)

  4. 更簡潔的錯誤處理


思考題:io_uring能否完全取代epoll?在哪些場景下epoll仍是必要選擇?

實戰挑戰:嘗試用io_uring實現一個簡單的HTTP服務器!

擴展閱讀:io_uring官方文檔 | liburing GitHub

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/90199.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/90199.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/90199.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

線段樹學習筆記 - 練習題(2)

文章目錄1. 前言2. P3870 [TJOI2009] 開關3. P2184 貪婪大陸4. P1438 無聊的數列5. P1471 方差1. 前言 線段樹系列文章&#xff1a; 線段樹學習筆記。線段樹學習筆記 - 練習題&#xff08;1&#xff09;。 前一篇做了幾道線段樹的題目&#xff0c;這篇文章就繼續看下線段樹的…

Vue狀態管理:Pinia 與 Vuex 的使用方法與對比【文章附有完整案例】

最近在接手vue項目的需求&#xff0c;因為之前一直在做react的需求&#xff0c;日常的vue練習也少了很多&#xff0c;導致現在接手vue項目&#xff0c;很多關于vue的知識點基本上忘得干干凈凈了。但是好在有基礎&#xff0c;重新學也會很快掌握。分享這個過程中的一些復習內容。…

OpenMed 項目深度分析:推動醫療 NLP 領域的開源革命

摘要 醫療人工智能(AI)領域因高質量數據和模型的獲取受限而發展緩慢。OpenMed 項目通過開源超過380個醫療命名實體識別(NER)模型,顯著降低了研究與應用門檻。本文從項目背景、技術優勢、應用場景、實施挑戰及未來展望五個方面,系統分析 OpenMed 的核心價值與潛力,揭示其…

大模型開發

什么是Ai&#xff1f;AI的全拼是(Artificial Intelligence)人工智能&#xff0c;使機器能夠像人類一樣思考、學習和解決問題的技術。在AI的應用情況下我們更多的是學習自然語言處理。在自然語言處理(Natural Language Processing&#xff0c;NLP)中&#xff0c;有一項關鍵技術叫…

【正常配置了beast擴展,phpinfo信息也顯示了,但是就是不運行】

正常配置了beast擴展&#xff0c;phpinfo信息也顯示了&#xff0c;但是就是不運行場景原因解決排查過程擴展場景 項目中使用到了beast進行源碼保護&#xff0c;指定類存在&#xff0c;但是報錯信息提示類找不到&#xff0c;beast擴展添加到了正在運行的php版本下的ext文件夾下…

CRMEB 單商戶PRO多商戶通用去版權教程

CRMEB去版權教程&#xff0c;此教程可根據具體版本進行調整&#xff0c;基本適用次方法。 后端版權修改 修改后端管理底部版權及門店后端管理底部版權。 文件位置 \view\admin\src\components\copyright\index.vue 文件位置 \view\admin\src\router\routes.js 文件位置 \vi…

舊物回收小程序系統開發:重塑舊物回收產業新生態

在傳統觀念中&#xff0c;舊物回收往往給人一種臟亂差、效率低下的印象&#xff0c;回收過程繁瑣&#xff0c;回收渠道有限&#xff0c;導致許多可回收物被浪費。然而&#xff0c;隨著信息技術的飛速發展&#xff0c;舊物回收小程序系統的開發正為這一古老行業帶來前所未有的變…

SSE和WebSocket區別到底是什么

文章目錄SSE 與 WebSocket&#xff1a;深入剖析兩者核心差異核心差異&#xff1a;單向 vs. 雙向通信技術細節對比協議與連接數據格式錯誤處理與可靠性適用場景&#xff1a;何時選擇 SSE&#xff0c;何時選擇 WebSocket&#xff1f;總結SSE 與 WebSocket&#xff1a;深入剖析兩者…

西安電子科技大學金融學431考研經歷分享

考研數學是區分度最大的科目之一&#xff0c;如何高效備考&#xff1f;本文為你推薦多位名師和經典書籍&#xff0c;助你在每個階段都能穩步提升&#xff0c;最終沖刺成功。一、考研數學備考策略教師推薦① 高等數學&#xff1a;② 線性代數&#xff1a;③ 概率論與數理統計&am…

laravel RedisException: Connection refused優雅草PMS項目管理系統報錯解決-以及Redis 詳細指南-優雅草卓伊凡

laravel RedisException: Connection refused優雅草PMS項目管理系統報錯解決-以及Redis 詳細指南-優雅草卓伊凡今天來開始更新pms系統&#xff0c;因為我們ppt上面要做&#xff0c;才發現原來打不開&#xff0c;此前主要是事情太多&#xff0c;我們一直有很多東西擱置解決 Lara…

拉力覆冰在線監測裝置:電力線路安全運行的數字化守衛者

在極端天氣頻發的背景下&#xff0c;輸電線路覆冰災害已成為威脅電網穩定運行的關鍵因素。拉力覆冰在線監測裝置通過數字化技術構建起全天候監測體系&#xff0c;為電力運維提供精準數據支撐。本文從技術實現與實際應用價值角度&#xff0c;解析該裝置的核心功能與行業意義。核…

AI面試如何提升物流行業招聘效率?實戰案例解析

每年秋招季&#xff0c;物流行業都會迎來海量應屆生簡歷涌入。面對業務快速擴張與人才篩選壓力&#xff0c;傳統線下面試流程長、標準模糊、成本高昂等問題愈發凸顯。本文通過兩大物流頭部企業的實戰案例&#xff0c;解析AI面試如何破解招聘困局&#xff0c;實現效率與質量的雙…

【機器學習】組合優化問題combination-optimization概述

博主簡介&#xff1a;努力學習的22級計算機科學與技術本科生一枚&#x1f338;博主主頁&#xff1a; Yaoyao2024往期回顧&#xff1a;【二分圖算法】手把手教你學會&#xff1a;染色法&#xff08;判斷二分圖&#xff09;、匈牙利算法&#xff08;二分圖的最大匹配&#xff09;…

Linux網絡編程-osi、udp

網絡&#xff1a;不同主機&#xff0c;進程間通信達到不同主機之間的困難&#xff1a;解決主機之間的硬件層面的互聯互通解決主機之間的軟件層面的互聯互通廣域網&#xff1a;進行大范圍網絡數據交換IP地址&#xff1a;區分不同主機 唯一的&#xff08;軟件地址&#xff09;MAC…

刪除 XML 格式中雙引號內的空格

要使用 Shell 命令刪除 XML 格式中雙引號內的空格&#xff08;僅處理屬性值中的空格&#xff0c;保留標簽外的空格&#xff09;&#xff0c;可以使用以下 sed 命令&#xff1a; sed -i :loop; s/\("[^"]*\) \([^"]*"\)/\1\2/g; t loop filename.xml命令詳解…

電腦聲音修復?【圖文詳解】電腦沒有聲音?聲音異常

一、問題背景 在使用電腦的過程中&#xff0c;聲音異常是很常見的問題。比如明明打開了音頻文件&#xff0c;卻聽不到任何聲音&#xff1b;或者聲音忽大忽小、伴有雜音&#xff1b;或者更新了聲卡驅動后&#xff0c;電腦播放不了聲音了&#xff1b;還有可能是插入耳機后&#x…

【文獻筆記】ARS: Automatic Routing Solver with Large Language Models

ARS: Automatic Routing Solver with Large Language Models https://github.com/Ahalikai/ARS-Routbench/ ARS&#xff1a;基于大語言模型的自動路由求解器 1. 概述 1.1. 研究背景 車輛路徑問題&#xff08;VRP&#xff09;是一類經典的組合優化問題&#xff0c;廣泛應用于…

RK3568筆記九十:基于web顯示RTSP流

若該文為原創文章,轉載請注明原文出處。 在網上看到個方案,使用web顯示RTSP視頻流,思路是前端傳入RTSP地址,cgi通過FFMPEG接收RTSP流并保存成avi文件,在通過ffmpeg 命令把avi文件保存成mp4文件,前端在播放mp4文件。此方案需要先保存文件,在轉換文件,無法實時播放。 所以…

2025年Flutter開發主流技術棧

2025年Flutter開發主流技術棧 Flutter作為一種高效、跨平臺的移動應用開發框架&#xff0c;近年來在開發者社區中越來越受歡迎。以下是2025年Flutter開發的主流技術棧&#xff0c;涵蓋了從核心框架到開發工具、狀態管理、數據存儲等多個方面。 1. 核心框架 Flutter&#xff1a;…

Qt 常用控件 - 1

控件概述 編程講究的是 --- 站在巨人的肩膀上 --- 不是編寫一個圖形化界面上的內容 --- Qt 已經提供了很多控件了&#xff01;&#xff01;&#xff01;提高圖形化界面的開發效率&#xff01;&#xff01;&#xff01;重點變成我們怎么使用這些已有的控件&#xff01; Widge…