2024年最全性能媲美epoll的io_uring_io_uring 和iocp(1),这里有份超全C C++体系化进阶学习图谱

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

		* 一般与`opcode`结合使用
	- `nr_args`
	
	
		* 一般与`opcode`结合使用
+ 返回值:


	- 成功返回`0`或一个特定值,取决于`opcode`的设置
	- 失败返回负数
  • io_uring_enter

    • 函数原型:

      int io_uring_enter(unsigned int fd,unsigned int to_submit,
      unsigned int min_complete,unsigned int flags,sigset_t *sig);

    • 函数作用:

      • 使用共享的 SQCQ初始化和完成IO
      • 单次调用同时执行:提交新的 I/O 请求;等待 I/O 完成。
    • 参数说明:

      • fd:io_uring_setup返回的文件描述符
      • to_submit:指定要从提交队列提交的I/O数。
    • 返回值:

      • 成功返回使用的I/O数量。如果to_submit为零或提交队列为空,则该值可以为零。注意,如果创建环形队列时指定了IORING_SETUP_SQPOLL,则返回值通常与to_submit相同,因为提交发生在系统调用的上下文之外。

liburing

内核提供了这几个接口,但是由于其参数复杂,opcode众多,因此它并不像epoll那样用起来轻量级,sqcq需要我们自己去构建,内存也需要我们自己去管理。这一套实现起来还是相当麻烦的。好在,网上早就有大神把这一套封装好了,liburing就是这样一套 开源库,我们可以直接拿去使用。

下载安装方法如下所示:

git clone https://github.com/axboe/liburing.git
./configure
make
sudo make install

io_uring实现高并发服务器

我们结合liburing,实现一个高并发服务器的代码如下:

#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;


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() {
    //创建服务器socket
	int sockfd = socket(AF_INET, SOCK_STREAM, 0); 	
	struct sockaddr\_in servaddr;
	memset(&servaddr, 0, sizeof(struct sockaddr\_in)); 
	servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    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); 
	
    /\*--------------------------------------------------------\*/
    // 构建io\_uring 相关参数
	struct io\_uring\_params params;
	memset(&params, 0, sizeof(params));
	
    // 初始化环形队列,mmap内存
	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);
	
    // 设置accept事件,监听sq队列是否有可读事件
	set\_accept\_event(&ring, sockfd, (struct sockaddr\*)&clientaddr, &clilen, 0);

	char buffer[1024] = {0};

	while (1) {
		// 相当于 io\_uring\_enter
		io\_uring\_submit(&ring);
		struct io\_uring\_cqe \*cqe;
		io\_uring\_wait\_cqe(&ring, &cqe);
		struct io\_uring\_cqe \*cqes[10];
        //一次从队列里获取一批数据,类似epoll\_wait的maxevents
		int cqecount = io\_uring\_peek\_batch\_cqe(&ring, cqes, 10);

		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) {  	//代表有新的连接上来
				if (cqe->res < 0) continue;

				int connfd = cqe->res;
                //设置accept事件,可读事件 
				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) {
                // 代表fd可读
				if (cqe->res < 0) continue;
				if (cqe->res == 0) {				
					close(ci.connfd);					
				} else {
                    //设置可写事件
					set\_send\_event(&ring, ci.connfd, buffer, cqe->res, 0);
				}

			} else if (ci.event == EVENT_WRITE) {
                //代表fd已经写成功,再次设置可读事件
				set\_recv\_event(&ring, ci.connfd, buffer, 1024, 0);
			}
		}
        // 这一步至关重要,它是将已经处理过的队列出队,避免重复处理队列中的事件
		io\_uring\_cq\_advance(&ring, cqecount);
	}
    close(sockfd);
    
    return 0;
}


总结

io_uring通过队列将sendrecv的数据做到异步解耦,从而提升了性能,但是相比于epollio_uring过分依赖于内核版本(kerner5.10以上),且操作相对比较繁琐。因此,现阶段可以作为了解,主流使用应该还是以epoll为主。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值