io_uring系统调用及示例

io_uring 库接口详解

基础接口

io_uring_queue_init(3)

NAME
       io_uring_queue_init - 初始化 io_uring 实例

SYNOPSIS
       #include <liburing.h>
       
       int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags);

DESCRIPTION
       初始化一个 io_uring 实例。entries 指定环形缓冲区的大小(必须是2的幂)。
       flags 可以是 IORING_SETUP_* 常量的组合。

RETURN VALUE
       成功返回0,失败返回负的错误码。

SEE ALSO
       io_uring_queue_exit(3)

io_uring_queue_exit(3)

NAME
       io_uring_queue_exit - 释放 io_uring 实例

SYNOPSIS
       #include <liburing.h>
       
       void io_uring_queue_exit(struct io_uring *ring);

DESCRIPTION
       释放之前初始化的 io_uring 实例,关闭相关文件描述符。

SEE ALSO
       io_uring_queue_init(3)

io_uring_get_sqe(3)

NAME
       io_uring_get_sqe - 获取提交队列项

SYNOPSIS
       #include <liburing.h>
       
       struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring);

DESCRIPTION
       从提交队列中获取一个可用的 SQE(提交队列项),用于构建 I/O 操作。

RETURN VALUE
       成功返回 SQE 指针,如果队列满则返回 NULL。

SEE ALSO
       io_uring_submit(3)

io_uring_submit(3)

NAME
       io_uring_submit - 提交 I/O 操作到内核

SYNOPSIS
       #include <liburing.h>
       
       int io_uring_submit(struct io_uring *ring);

DESCRIPTION
       将准备好的 SQE 提交到内核执行。返回提交的操作数量。

RETURN VALUE
       成功返回提交的操作数,失败返回负的错误码。

SEE ALSO
       io_uring_wait_cqe(3)

io_uring_wait_cqe(3)

NAME
       io_uring_wait_cqe - 等待完成队列项

SYNOPSIS
       #include <liburing.h>
       
       int io_uring_wait_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr);

DESCRIPTION
       等待至少一个 I/O 操作完成,并返回对应的 CQE(完成队列项)。

RETURN VALUE
       成功返回0,失败返回负的错误码。

SEE ALSO
       io_uring_peek_cqe(3), io_uring_cqe_seen(3)

io_uring_peek_cqe(3)

NAME
       io_uring_peek_cqe - 非阻塞检查完成队列项

SYNOPSIS
       #include <liburing.h>
       
       int io_uring_peek_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr);

DESCRIPTION
       非阻塞地检查是否有完成的 I/O 操作,如果有则返回对应的 CQE。

RETURN VALUE
       成功返回0,没有完成项返回 -EAGAIN,其他错误返回负的错误码。

SEE ALSO
       io_uring_wait_cqe(3)

io_uring_cqe_seen(3)

NAME
       io_uring_cqe_seen - 标记完成队列项已处理

SYNOPSIS
       #include <liburing.h>
       
       void io_uring_cqe_seen(struct io_uring *ring, struct io_uring_cqe *cqe);

DESCRIPTION
       标记指定的 CQE 已被处理,从完成队列中移除。

SEE ALSO
       io_uring_wait_cqe(3)

I/O 操作接口

io_uring_prep_read(3)

NAME
       io_uring_prep_read - 准备读操作

SYNOPSIS
       #include <liburing.h>
       
       void io_uring_prep_read(struct io_uring_sqe *sqe, int fd, void *buf, 
                              unsigned nbytes, __u64 offset);

DESCRIPTION
       准备一个异步读操作。从 fd 读取 nbytes 字节到 buf,从 offset 开始。

SEE ALSO
       io_uring_prep_write(3)

io_uring_prep_write(3)

NAME
       io_uring_prep_write - 准备写操作

SYNOPSIS
       #include <liburing.h>
       
       void io_uring_prep_write(struct io_uring_sqe *sqe, int fd, const void *buf,
                               unsigned nbytes, __u64 offset);

DESCRIPTION
       准备一个异步写操作。将 buf 中的 nbytes 字节写入 fd,从 offset 开始。

SEE ALSO
       io_uring_prep_read(3)

io_uring_prep_openat(3)

NAME
       io_uring_prep_openat - 准备文件打开操作

SYNOPSIS
       #include <liburing.h>
       
       void io_uring_prep_openat(struct io_uring_sqe *sqe, int dfd, const char *path,
                                int flags, mode_t mode);

DESCRIPTION
       准备一个异步文件打开操作。dfd 是目录文件描述符,path 是相对路径。

SEE ALSO
       io_uring_prep_close(3)

io_uring_prep_close(3)

NAME
       io_uring_prep_close - 准备文件关闭操作

SYNOPSIS
       #include <liburing.h>
       
       void io_uring_prep_close(struct io_uring_sqe *sqe, int fd);

DESCRIPTION
       准备一个异步文件关闭操作。

SEE ALSO
       io_uring_prep_openat(3)

io_uring 使用示例

示例1:异步文件读写

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <liburing.h>

#define QUEUE_DEPTH 32
#define BLOCK_SIZE 4096

int async_file_copy(const char *src, const char *dst) {
    struct io_uring ring;
    int src_fd, dst_fd;
    char *buffer;
    struct io_uring_sqe *sqe;
    struct io_uring_cqe *cqe;
    int ret;

    // 初始化 io_uring
    ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
    if (ret < 0) {
        fprintf(stderr, "queue_init: %s\n", strerror(-ret));
        return 1;
    }

    // 打开源文件和目标文件
    src_fd = open(src, O_RDONLY);
    if (src_fd < 0) {
        perror("open src");
        goto cleanup_ring;
    }

    dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (dst_fd < 0) {
        perror("open dst");
        goto cleanup_src;
    }

    // 分配缓冲区
    buffer = malloc(BLOCK_SIZE);
    if (!buffer) {
        perror("malloc");
        goto cleanup_dst;
    }

    // 异步读取和写入
    off_t offset = 0;
    ssize_t bytes_read;

    while (1) {
        // 提交读操作
        sqe = io_uring_get_sqe(&ring);
        if (!sqe) {
            fprintf(stderr, "get_sqe failed\n");
            break;
        }
        
        io_uring_prep_read(sqe, src_fd, buffer, BLOCK_SIZE, offset);
        sqe->user_data = 1; // 标识读操作

        ret = io_uring_submit(&ring);
        if (ret <= 0) {
            fprintf(stderr, "submit read failed: %s\n", strerror(-ret));
            break;
        }

        // 等待读操作完成
        ret = io_uring_wait_cqe(&ring, &cqe);
        if (ret < 0) {
            fprintf(stderr, "wait_cqe failed: %s\n", strerror(-ret));
            break;
        }

        bytes_read = cqe->res;
        io_uring_cqe_seen(&ring, cqe);

        if (bytes_read <= 0) {
            // 文件读取完成
            break;
        }

        // 提交写操作
        sqe = io_uring_get_sqe(&ring);
        if (!sqe) {
            fprintf(stderr, "get_sqe failed\n");
            break;
        }
        
        io_uring_prep_write(sqe, dst_fd, buffer, bytes_read, offset);
        sqe->user_data = 2; // 标识写操作

        ret = io_uring_submit(&ring);
        if (ret <= 0) {
            fprintf(stderr, "submit write failed: %s\n", strerror(-ret));
            break;
        }

        // 等待写操作完成
        ret = io_uring_wait_cqe(&ring, &cqe);
        if (ret < 0) {
            fprintf(stderr, "wait_cqe failed: %s\n", strerror(-ret));
            break;
        }

        if (cqe->res < 0) {
            fprintf(stderr, "write failed: %s\n", strerror(-cqe->res));
            io_uring_cqe_seen(&ring, cqe);
            break;
        }

        io_uring_cqe_seen(&ring, cqe);
        offset += bytes_read;
    }

    free(buffer);
    close(dst_fd);
    close(src_fd);
    io_uring_queue_exit(&ring);
    return 0;

cleanup_dst:
    close(dst_fd);
cleanup_src:
    close(src_fd);
cleanup_ring:
    io_uring_queue_exit(&ring);
    return 1;
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <src> <dst>\n", argv[0]);
        return 1;
    }

    return async_file_copy(argv[1], argv[2]);
}

示例2:并发网络服务器

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <liburing.h>

#define MAX_CONNECTIONS 1024
#define BUFFER_SIZE 4096
#define QUEUE_DEPTH 256

struct conn_info {
    int fd;
    char buffer[BUFFER_SIZE];
};

int setup_listening_socket(int port) {
    int sock;
    struct sockaddr_in addr;
    int opt = 1;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket");
        return -1;
    }

    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        perror("setsockopt");
        close(sock);
        return -1;
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(sock);
        return -1;
    }

    if (listen(sock, 128) < 0) {
        perror("listen");
        close(sock);
        return -1;
    }

    return sock;
}

void add_accept_request(struct io_uring *ring, int listen_fd, struct conn_info *conn_i) {
    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
    io_uring_prep_accept(sqe, listen_fd, NULL, NULL, 0);
    sqe->user_data = (uint64_t)(uintptr_t)conn_i;
}

void add_read_request(struct io_uring *ring, int fd, struct conn_info *conn_i) {
    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
    io_uring_prep_read(sqe, fd, conn_i->buffer, BUFFER_SIZE - 1, 0);
    sqe->user_data = (uint64_t)(uintptr_t)conn_i | 1UL << 63;
}

void add_write_request(struct io_uring *ring, int fd, struct conn_info *conn_i, size_t len) {
    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
    io_uring_prep_write(sqe, fd, conn_i->buffer, len, 0);
    sqe->user_data = (uint64_t)(uintptr_t)conn_i | 1UL << 62;
}

int main() {
    struct io_uring ring;
    int listen_fd;
    struct conn_info *conn_infos;
    int i;

    // 初始化 io_uring
    if (io_uring_queue_init(QUEUE_DEPTH, &ring, 0) < 0) {
        perror("io_uring_queue_init");
        return 1;
    }

    // 创建监听套接字
    listen_fd = setup_listening_socket(8080);
    if (listen_fd < 0) {
        return 1;
    }

    printf("Server listening on port 8080\n");

    // 分配连接信息结构
    conn_infos = malloc(MAX_CONNECTIONS * sizeof(struct conn_info));
    if (!conn_infos) {
        perror("malloc");
        close(listen_fd);
        io_uring_queue_exit(&ring);
        return 1;
    }

    // 提交初始的 accept 请求
    for (i = 0; i < MAX_CONNECTIONS; i++) {
        add_accept_request(&ring, listen_fd, &conn_infos[i]);
    }

    io_uring_submit(&ring);

    // 事件循环
    while (1) {
        struct io_uring_cqe *cqe;
        struct conn_info *conn_i;
        int type;

        if (io_uring_wait_cqe(&ring, &cqe) < 0) {
            perror("io_uring_wait_cqe");
            continue;
        }

        conn_i = (struct conn_info*)(uintptr_t)(cqe->user_data & ~(1UL << 63) & ~(1UL << 62));
        type = cqe->user_data & (1UL << 63) ? 1 : (cqe->user_data & (1UL << 62) ? 2 : 0);

        switch (type) {
            case 0: // accept
                if (cqe->res > 0) {
                    int client_fd = cqe->res;
                    printf("New connection: %d\n", client_fd);
                    add_read_request(&ring, client_fd, conn_i);
                } else {
                    printf("Accept failed: %s\n", strerror(-cqe->res));
                }
                // 重新提交 accept 请求
                add_accept_request(&ring, listen_fd, conn_i);
                break;

            case 1: // read
                if (cqe->res > 0) {
                    ssize_t bytes_read = cqe->res;
                    conn_i->buffer[bytes_read] = '\0';
                    printf("Read %zd bytes: %s\n", bytes_read, conn_i->buffer);
                    
                    // Echo back
                    add_write_request(&ring, conn_i->fd, conn_i, bytes_read);
                } else {
                    // Connection closed or error
                    if (cqe->res < 0) {
                        printf("Read failed: %s\n", strerror(-cqe->res));
                    }
                    close(conn_i->fd);
                }
                break;

            case 2: // write
                if (cqe->res < 0) {
                    printf("Write failed: %s\n", strerror(-cqe->res));
                }
                // 继续读取
                add_read_request(&ring, conn_i->fd, conn_i);
                break;
        }

        io_uring_cqe_seen(&ring, cqe);
        io_uring_submit(&ring);
    }

    free(conn_infos);
    close(listen_fd);
    io_uring_queue_exit(&ring);
    return 0;
}

示例3:批量文件操作

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <liburing.h>
#include <sys/stat.h>

#define QUEUE_DEPTH 64
#define MAX_FILES 100

struct file_info {
    int fd;
    char filename[256];
    char *buffer;
    size_t size;
};

int async_file_operations() {
    struct io_uring ring;
    struct file_info files[MAX_FILES];
    struct io_uring_sqe *sqe;
    struct io_uring_cqe *cqe;
    int i, ret;
    int submitted = 0;

    // 初始化 io_uring
    ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
    if (ret < 0) {
        fprintf(stderr, "queue_init: %s\n", strerror(-ret));
        return 1;
    }

    // 准备文件信息
    for (i = 0; i < MAX_FILES; i++) {
        snprintf(files[i].filename, sizeof(files[i].filename), 
                "test_file_%d.txt", i);
        files[i].buffer = malloc(1024);
        sprintf(files[i].buffer, "Content of file %d\n", i);
        files[i].size = strlen(files[i].buffer);
    }

    // 批量提交文件创建和写入操作
    for (i = 0; i < MAX_FILES; i++) {
        // 打开文件
        sqe = io_uring_get_sqe(&ring);
        if (!sqe) {
            fprintf(stderr, "get_sqe failed\n");
            break;
        }
        io_uring_prep_openat(sqe, AT_FDCWD, files[i].filename, 
                           O_WRONLY | O_CREAT | O_TRUNC, 0644);
        sqe->user_data = (uint64_t)(i * 2); // 偶数标识符表示打开操作

        // 写入文件
        sqe = io_uring_get_sqe(&ring);
        if (!sqe) {
            fprintf(stderr, "get_sqe failed\n");
            break;
        }
        // 注意:这里需要先提交打开操作,获取fd后再写入
        // 为了简化示例,我们假设文件已存在
        
        submitted += 2;
    }

    // 提交所有操作
    ret = io_uring_submit(&ring);
    if (ret < 0) {
        fprintf(stderr, "submit failed: %s\n", strerror(-ret));
        goto cleanup;
    }

    // 收集完成结果
    for (i = 0; i < submitted; i++) {
        ret = io_uring_wait_cqe(&ring, &cqe);
        if (ret < 0) {
            fprintf(stderr, "wait_cqe failed: %s\n", strerror(-ret));
            continue;
        }

        if (cqe->res < 0) {
            fprintf(stderr, "Operation failed: %s (user_data: %lu)\n", 
                   strerror(-cqe->res), cqe->user_data);
        } else {
            printf("Operation completed successfully (user_data: %lu, result: %d)\n", 
                   cqe->user_data, cqe->res);
        }

        io_uring_cqe_seen(&ring, cqe);
    }

cleanup:
    // 清理资源
    for (i = 0; i < MAX_FILES; i++) {
        if (files[i].buffer) {
            free(files[i].buffer);
        }
        // 删除测试文件
        unlink(files[i].filename);
    }

    io_uring_queue_exit(&ring);
    return 0;
}

int main() {
    return async_file_operations();
}

io_uring 使用限制条件

内核版本要求

  • Linux 5.1+: 基础功能
  • Linux 5.5+: 更多操作类型支持
  • Linux 5.6+: 性能优化
  • Linux 5.7+: 更完整的功能集

硬件和系统要求

  1. CPU架构: 支持 x86_64, ARM64, RISC-V 等现代架构
  2. 内存: 需要足够的连续内存用于环形缓冲区
  3. 文件系统: 支持大多数现代文件系统(ext4, xfs, btrfs等)

功能限制

  1. 队列大小: 必须是2的幂,最大通常为4096
  2. 并发限制: 受系统资源和内核参数限制
  3. 操作类型: 并非所有系统调用都支持异步化

内存管理限制

  1. 缓冲区生命周期: 应用程序必须确保缓冲区在I/O完成前有效
  2. 内存对齐: 某些操作可能需要特定的内存对齐
  3. 大内存页: 建议使用大页以提高性能

并发和同步限制

  1. 线程安全: 同一 ring 不能被多个线程同时访问
  2. 信号处理: 在信号处理程序中使用需要特别注意
  3. 资源竞争: 需要正确处理多个 ring 之间的资源共享

错误处理限制

  1. 错误报告: 某些错误可能不会立即报告
  2. 恢复机制: 部分错误后可能需要重新初始化
  3. 资源泄漏: 错误处理不当可能导致资源泄漏

性能考虑

  1. 批处理: 小批量操作可能不如直接系统调用快
  2. 延迟: 第一次使用可能有初始化开销
  3. CPU亲和性: 可能受益于CPU绑定优化

这些限制条件在实际使用中需要仔细考虑,以确保 io_uring 能够正确和高效地工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值