既有适合小白学习的零基础资料,也有适合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); -
-
函数作用:
- 使用共享的
SQ
和CQ
初始化和完成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
那样用起来轻量级,sq
和cq
需要我们自己去构建,内存也需要我们自己去管理。这一套实现起来还是相当麻烦的。好在,网上早就有大神把这一套封装好了,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(¶ms, 0, sizeof(params));
// 初始化环形队列,mmap内存
struct io\_uring ring;
io\_uring\_queue\_init\_params(ENTRIES_LENGTH, &ring, ¶ms);
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
通过队列将send
和recv
的数据做到异步解耦,从而提升了性能,但是相比于epoll
,io_uring
过分依赖于内核版本(kerner5.10
以上),且操作相对比较繁琐。因此,现阶段可以作为了解,主流使用应该还是以epoll
为主。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!