2024年最全性能媲美epoll的io_uring_io_uring 和iocp(2),2024年最新万字长文轻松彻底入门Flutter

img
img

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

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

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

	int io\_uring\_register(unsigned int fd, unsigned int opcode, void \*arg, unsigned int nr_args)
	
	```
+ 函数作用:


	- 注册用于异步`IO`的文件或用户缓冲区,使内核能长时间持有对该文件 在内核内部的数据结构引用,或创建应用内存的长期映射
		* 这个操作只会在注册时执行一次,而不是每个`IO`请求都会处理,因此减少了`IO`开销
+ 参数说明:


	- `fd`:`io_uring_setup`返回的文件描述符
	- `opcode`:操作代码,用来指定被锁定资源的类型及方式, 常用的如:
	
	
		* `IORING_REGISTER_BUFFERS`
			+ `arg`指向一个包含`nr_args`向的`struct iovec`数组,与`iovec`相关的缓冲区将被锁定在内存中
		* `IORING_REGISTER_BUFFERS2`
			+ `arg`指向`io_uring_rsrc_register`结构体,`nr_args`应该设置为结构体中的字节数
		* `IORING_REGISTER_BUFFERS_UPDATE`
			+ 用新的缓冲区更新已注册的缓冲区,要么将稀疏条目转换为真实条目,要么替换现有条目。
			+ `arg`必须包含一个指向`io_uring_rsrc_update2`结构体的指针,其中包含开始更新的偏移量,以及一个数组  
			 结构。
	- `arg`
	
	
		* 一般与`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) {				


![img](https://img-blog.csdnimg.cn/img_convert/906f3a531d4eb8e4e127c897ac0efe2c.png)
![img](https://img-blog.csdnimg.cn/img_convert/bdf2504e703d54488e4fa77833f0704d.png)

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

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.youkuaiyun.com/topics/618668825)**


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

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

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.youkuaiyun.com/topics/618668825)**


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值