了解一下与epoll媲美的io_uring

简介: 了解一下与epoll媲美的io_uring

1 io_uring是Linux内核的一个新型I/O事件通知机制,具有以下特点:

高性能:相比传统的select/poll/epoll等I/O多路复用机制,io_uring采用了更高效的ring buffer实现方式,可以在处理大量并发I/O请求时提供更高的吞吐量和低延迟。

异步:io_uring支持异步I/O操作,并且可以通过用户空间和内核空间之间的共享内存映射来避免数据拷贝,从而减少了CPU的开销。

事件批处理:io_uring可以将多个I/O操作合并成一个请求进行处理,从而降低了系统调用的次数和上下文切换的开销。

灵活性:io_uring提供了非常灵活的接口和配置选项,可以根据应用程序的需要进行优化和调整。同时,它还支持多线程操作和信号驱动I/O等功能。

2 安装

确认系统内核是5.10以后的版本,还需安装liburing(依赖于内核的三个新的系统调用
io_uring_setup io_uring_register io_uring_enter )
安装liburing库,它对内核三个新的系统进行了封装
git clone https://github.com/axboe/liburing.git
cd liburing/
./configure
make && make install

3 原理图

sq 与 cq 都是环型的队列(这里一看到队列,就要想到它起到异步解耦的作用), 读写都是异步的
在这里插入图片描述
submit queue 任务提交队列
complete queue 任务处理完成的队列
sq 与 cq 是共用相同的节点,sq到cq没有拷贝操作,而且节点所占用的内存是内核空间与用户空间共享的
(看源码可得:内存是在内核空间开辟的,然后映射到用户空间)

4 源码

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

#include <liburing.h>

#define ENTRIES_LENGTH        1024

enum {
   
   
    EVENT_ACCEPT = 0,
    EVENT_READ,
    EVENT_WRITE
};

typedef struct _conninfo {
   
   
    int connfd;
    int event;
} conninfo;

// sizeof(conninfo)  = 8

// 0, 1, 2
// 3, 4, 5

void set_send_event(struct io_uring *ring, int sockfd, void *buf, size_t len, int flags) {
   
   

    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    io_uring_prep_send(sqe, sockfd, buf, len, flags);
    conninfo info_send = {
   
   
        .connfd = sockfd,
        .event = EVENT_WRITE,
    };
    memcpy(&sqe->user_data, &info_send, sizeof(info_send));

}


void set_recv_event(struct io_uring *ring, int sockfd, void *buf, size_t len, int flags) {
   
   

    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    io_uring_prep_recv(sqe, sockfd, buf, len, flags);
    conninfo info_recv = {
   
   
        .connfd = sockfd,
        .event = EVENT_READ,
    };
    memcpy(&sqe->user_data, &info_recv, sizeof(info_recv));

}

void set_accept_event(struct io_uring *ring, int sockfd, struct sockaddr *addr,
                   socklen_t *addrlen, int flags) {
   
   

    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    io_uring_prep_accept(sqe, sockfd, addr, addrlen, flags);
    conninfo info_accept = {
   
   
        .connfd = sockfd,
        .event = EVENT_ACCEPT,
    };
    memcpy(&sqe->user_data, &info_accept, sizeof(info_accept));

}


int main() {
   
   

    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // io

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(struct sockaddr_in)); // 192.168.2.123
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0
    servaddr.sin_port = htons(9999);

    if (-1 == bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr))) {
   
   
        printf("bind failed: %s", strerror(errno));
        return -1;
    }

    listen(sockfd, 10); 
//liburing

    struct io_uring_params params;
    memset(&params, 0, sizeof(params));

    struct io_uring ring;
    io_uring_queue_init_params(ENTRIES_LENGTH, &ring, &params);

    struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);

    struct sockaddr_in clientaddr;
    socklen_t clilen = sizeof(struct sockaddr);

    set_accept_event(&ring, sockfd, (struct sockaddr*)&clientaddr, &clilen, 0);

    char buffer[1024] = {
   
   0};

    while (1) {
   
   

        io_uring_submit(&ring);

        struct io_uring_cqe *cqe;
        io_uring_wait_cqe(&ring, &cqe);

        struct io_uring_cqe *cqes[10];
        int cqecount = io_uring_peek_batch_cqe(&ring, cqes, 10);

        //printf("cqecount --> %d\n", cqecount);
        int i = 0;
        for (i = 0;i < cqecount;i ++) {
   
   

            cqe = cqes[i];

            conninfo ci;
            memcpy(&ci, &cqe->user_data, sizeof(ci));

            if (ci.event == EVENT_ACCEPT) {
   
     // recv/send


                if (cqe->res < 0) continue;

                int connfd = cqe->res;
                //printf("accept --> %d\n", connfd);
                set_accept_event(&ring, ci.connfd, (struct sockaddr*)&clientaddr, &clilen, 0);

                set_recv_event(&ring, connfd, buffer, 1024, 0);

            } else if (ci.event == EVENT_READ) {
   
   

                if (cqe->res < 0) continue;
                if (cqe->res == 0) {
   
   

                    close(ci.connfd);

                } else {
   
   

                    printf("recv --> %s, %d\n", buffer, cqe->res);

                    //set_recv_event(&ring, ci.connfd, buffer, 1024, 0);
                    set_send_event(&ring, ci.connfd, buffer, cqe->res, 0);
                }

            } else if (ci.event == EVENT_WRITE) {
   
    //


                //printf("write complete\n");
                set_recv_event(&ring, ci.connfd, buffer, 1024, 0);

            }    
        }
        io_uring_cq_advance(&ring, cqecount);
    }
    getchar();

}

5 结果演示

编译 gcc -o uring_server uring_server.c -luring -static
运行 ./uring_server, 借助网络调试助手测试
在这里插入图片描述
文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:链接

相关文章
|
2月前
|
网络协议 安全 测试技术
手撕测试tcp服务器效率工具——以epoll和io_uring对比为例
手撕测试tcp服务器效率工具——以epoll和io_uring对比为例
44 2
|
3月前
|
存储 Linux 调度
io复用之epoll核心源码剖析
epoll底层实现中有两个关键的数据结构,一个是eventpoll另一个是epitem,其中eventpoll中有两个成员变量分别是rbr和rdlist,前者指向一颗红黑树的根,后者指向双向链表的头。而epitem则是红黑树节点和双向链表节点的综合体,也就是说epitem即可作为树的节点,又可以作为链表的节点,并且epitem中包含着用户注册的事件。当用户调用epoll_create()时,会创建eventpoll对象(包含一个红黑树和一个双链表);
72 0
io复用之epoll核心源码剖析
|
3月前
|
存储 网络协议
TCP服务器 IO多路复用的实现:select、poll、epoll
TCP服务器 IO多路复用的实现:select、poll、epoll
36 0
|
3月前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
59 0
|
5月前
IO多路转接 ——— select、poll、epoll(下)
IO多路转接 ——— select、poll、epoll
IO多路转接 ——— select、poll、epoll(下)
|
4月前
|
存储 Linux
图解IO多路复用模型之select、poll、epoll
图解IO多路复用模型之select、poll、epoll
53 0
|
4月前
|
存储 网络协议
与epoll媲美的io_uring
与epoll媲美的io_uring
34 0
|
1月前
|
NoSQL Java Linux
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
64 0
|
3月前
|
消息中间件 架构师 Java
性能媲美epoll的io_uring
性能媲美epoll的io_uring
42 0
|
3月前
|
网络协议 架构师 Linux
一文说透IO多路复用select/poll/epoll
一文说透IO多路复用select/poll/epoll
159 0