io_uring场景

  • io_uring和epoll的区别
  • io_uring 与epoll性能比较
    • 安装rust_echo_bench测试工具
    • 测试比较
  • 实现封装io_uring用户态文件读写接口
    • 系统调用
    • 内存映射mmap
    • SQ_RING、CQ_RING、SQES关系
    • 代码实现示例
  • 总结
  • 后言

io_uring和epoll的区别

(1)epoll设置事件完成之后,以后只要不修改或删除事件,就可以一直等待IO事件触发。即事件驱动机制。
epoll对事件的管理使用的是红黑树。

#mermaid-svg-133T5WB869gNHoXq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-133T5WB869gNHoXq .error-icon{fill:#552222;}#mermaid-svg-133T5WB869gNHoXq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-133T5WB869gNHoXq .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-133T5WB869gNHoXq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-133T5WB869gNHoXq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-133T5WB869gNHoXq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-133T5WB869gNHoXq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-133T5WB869gNHoXq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-133T5WB869gNHoXq .marker.cross{stroke:#333333;}#mermaid-svg-133T5WB869gNHoXq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-133T5WB869gNHoXq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-133T5WB869gNHoXq .cluster-label text{fill:#333;}#mermaid-svg-133T5WB869gNHoXq .cluster-label span{color:#333;}#mermaid-svg-133T5WB869gNHoXq .label text,#mermaid-svg-133T5WB869gNHoXq span{fill:#333;color:#333;}#mermaid-svg-133T5WB869gNHoXq .node rect,#mermaid-svg-133T5WB869gNHoXq .node circle,#mermaid-svg-133T5WB869gNHoXq .node ellipse,#mermaid-svg-133T5WB869gNHoXq .node polygon,#mermaid-svg-133T5WB869gNHoXq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-133T5WB869gNHoXq .node .label{text-align:center;}#mermaid-svg-133T5WB869gNHoXq .node.clickable{cursor:pointer;}#mermaid-svg-133T5WB869gNHoXq .arrowheadPath{fill:#333333;}#mermaid-svg-133T5WB869gNHoXq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-133T5WB869gNHoXq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-133T5WB869gNHoXq .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-133T5WB869gNHoXq .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-133T5WB869gNHoXq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-133T5WB869gNHoXq .cluster text{fill:#333;}#mermaid-svg-133T5WB869gNHoXq .cluster span{color:#333;}#mermaid-svg-133T5WB869gNHoXq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-133T5WB869gNHoXq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

TCP Server
listenfd
socket
listen
socket
bind
epoll
while
while !quit
连接
epoll_ctl 添加读事件
epoll_wait
accpet
recv
epoll_ctl 添加写事件
send
epoll_ctl 添加读事件
epoll_create
epoll_ctl 添加listenfd事件

(2)io_uring有两个队列,SQ和CQ,io_uring_submite之后,事件提交在SQ等待,事件达到后交给CQ,应用程序调用io_uring_peek_batch_cqe从CQ取出后,会调用io_uring_cq_advance将事件触发销毁,因此要想一直可以等待事件,需要从CQ取出后再次把事件加入SQ中。即异步机制。
io_uring对事件的管理采用两个队列:SQ(submition queue)和CQ(completion queue)。

#mermaid-svg-9WmIfZhD9j7iEoW7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-9WmIfZhD9j7iEoW7 .error-icon{fill:#552222;}#mermaid-svg-9WmIfZhD9j7iEoW7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9WmIfZhD9j7iEoW7 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-9WmIfZhD9j7iEoW7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9WmIfZhD9j7iEoW7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9WmIfZhD9j7iEoW7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9WmIfZhD9j7iEoW7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9WmIfZhD9j7iEoW7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9WmIfZhD9j7iEoW7 .marker.cross{stroke:#333333;}#mermaid-svg-9WmIfZhD9j7iEoW7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9WmIfZhD9j7iEoW7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9WmIfZhD9j7iEoW7 .cluster-label text{fill:#333;}#mermaid-svg-9WmIfZhD9j7iEoW7 .cluster-label span{color:#333;}#mermaid-svg-9WmIfZhD9j7iEoW7 .label text,#mermaid-svg-9WmIfZhD9j7iEoW7 span{fill:#333;color:#333;}#mermaid-svg-9WmIfZhD9j7iEoW7 .node rect,#mermaid-svg-9WmIfZhD9j7iEoW7 .node circle,#mermaid-svg-9WmIfZhD9j7iEoW7 .node ellipse,#mermaid-svg-9WmIfZhD9j7iEoW7 .node polygon,#mermaid-svg-9WmIfZhD9j7iEoW7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9WmIfZhD9j7iEoW7 .node .label{text-align:center;}#mermaid-svg-9WmIfZhD9j7iEoW7 .node.clickable{cursor:pointer;}#mermaid-svg-9WmIfZhD9j7iEoW7 .arrowheadPath{fill:#333333;}#mermaid-svg-9WmIfZhD9j7iEoW7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9WmIfZhD9j7iEoW7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9WmIfZhD9j7iEoW7 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-9WmIfZhD9j7iEoW7 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-9WmIfZhD9j7iEoW7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9WmIfZhD9j7iEoW7 .cluster text{fill:#333;}#mermaid-svg-9WmIfZhD9j7iEoW7 .cluster span{color:#333;}#mermaid-svg-9WmIfZhD9j7iEoW7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9WmIfZhD9j7iEoW7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

TCP Server
listenfd
socket
listen
socket
bind
io_uring
while
io_uring while
io_uring_peek_patch_cqe
io_uring_submit
io_uring_wait_cqe
io_uring_prep_recv
io_uring_prep_send
io_uring_prep_acceptd
io_uring_cq_advance
io_uring_queue_init_params
io_uring_prep_accept

io_uring 与epoll性能比较

安装rust_echo_bench测试工具

(1)安装cargo
ubuntu

sudo apt-get install cargo

centos

sudo yum install cargo

(2)下载rust_echo_bench

git clone https://github.com/haraldh/rust_echo_bench.git

(3)编译rust_echo_bench

cd rust_echo_bench
cargo run --release -- --help

过程

    Updating crates.io indexDownloaded unicode-width v0.1.9Downloaded getopts v0.2.21Downloaded 2 crates (35.2 KB) in 1.58sCompiling unicode-width v0.1.9Compiling getopts v0.2.21Compiling echo_bench v0.2.0 (/home/user/rust_echo_bench)Finished release [optimized] target(s) in 4m 05sRunning `target/release/echo_bench --help`
Echo benchmark.Usage:target/release/echo_bench [ -a <address> ] [ -l <length> ] [ -c <number> ] [ -t <duration> ]target/release/echo_bench (-h | --help)target/release/echo_bench --versionOptions:-h, --help          Print this help.-a, --address <address>Target echo server address. Default: 127.0.0.1:12345-l, --length <length>Test message length. Default: 512-t, --duration <duration>Test duration in seconds. Default: 60-c, --number <number>Test connection number. Default: 50

(3)运行

cargo run --release -- --address "192.168.7.233:9999" --number 1000 --duration 60 --length 512

将相关参数修改即可

测试比较

io_uring结果:

    Finished release [optimized] target(s) in 0.00sRunning `target/release/echo_bench --address '192.168.7.233:9999' --number 1000 --duration 60 --length 512`
Benchmarking: 192.168.7.235:9999
1000 clients, running 512 bytes, 60 sec.Speed: 8836 request/sec, 8836 response/sec
Requests: 530176
Responses: 530176

epoll测试前,需要先调整ulimit大小:ulimit -n 1024567。
epoll结果:

    Finished release [optimized] target(s) in 0.01sRunning `target/release/echo_bench --address '192.168.7.233:8888' --number 1000 --duration 60 --length 512`
Benchmarking: 192.168.7.233:8888
1000 clients, running 512 bytes, 60 sec.Speed: 7908 request/sec, 7908 response/sec
Requests: 474517
Responses: 474516

注意,此测试结果仅仅是某次数据或访问的比较,为自己编写的代码提供一种测试方案,不作为说明io_uring与epoll/poll/select的性能高低。

小结:io_uring在性能上不比reactor高多少,io_uring不一定会完全替代epoll,未来是io_uring与epoll并存,只是网络IO事件的处理方案多了一个选择。

实现封装io_uring用户态文件读写接口

io_uring提供三个系统调用接口:io_uring_submit()、io_uring_enter()、io_uring_register()。不使用liburing情况下,需要自己实现用户层的接口。io_uring除了可以处理网络IO,也可以处理磁盘IO事件。

系统调用

系统调用函数是syscall。调用syscall函数时,会触发一个0x80的中断。每个系统调用都有系统调用号,存放在sys_call_table数组中。
流程:调用syscall,触发0x80中断,将系统调用号赋给eax寄存器,参数赋给ebx,ecx等寄存器,然后执行system_call。

#mermaid-svg-vZHS2nVutxcqZKPq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-vZHS2nVutxcqZKPq .error-icon{fill:#552222;}#mermaid-svg-vZHS2nVutxcqZKPq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vZHS2nVutxcqZKPq .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-vZHS2nVutxcqZKPq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vZHS2nVutxcqZKPq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vZHS2nVutxcqZKPq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vZHS2nVutxcqZKPq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vZHS2nVutxcqZKPq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vZHS2nVutxcqZKPq .marker.cross{stroke:#333333;}#mermaid-svg-vZHS2nVutxcqZKPq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vZHS2nVutxcqZKPq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vZHS2nVutxcqZKPq .cluster-label text{fill:#333;}#mermaid-svg-vZHS2nVutxcqZKPq .cluster-label span{color:#333;}#mermaid-svg-vZHS2nVutxcqZKPq .label text,#mermaid-svg-vZHS2nVutxcqZKPq span{fill:#333;color:#333;}#mermaid-svg-vZHS2nVutxcqZKPq .node rect,#mermaid-svg-vZHS2nVutxcqZKPq .node circle,#mermaid-svg-vZHS2nVutxcqZKPq .node ellipse,#mermaid-svg-vZHS2nVutxcqZKPq .node polygon,#mermaid-svg-vZHS2nVutxcqZKPq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vZHS2nVutxcqZKPq .node .label{text-align:center;}#mermaid-svg-vZHS2nVutxcqZKPq .node.clickable{cursor:pointer;}#mermaid-svg-vZHS2nVutxcqZKPq .arrowheadPath{fill:#333333;}#mermaid-svg-vZHS2nVutxcqZKPq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vZHS2nVutxcqZKPq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vZHS2nVutxcqZKPq .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-vZHS2nVutxcqZKPq .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-vZHS2nVutxcqZKPq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vZHS2nVutxcqZKPq .cluster text{fill:#333;}#mermaid-svg-vZHS2nVutxcqZKPq .cluster span{color:#333;}#mermaid-svg-vZHS2nVutxcqZKPq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vZHS2nVutxcqZKPq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

触发
寄存器操作
寄存器操作
执行
执行
syscall
0x80中断
系统调用号赋给eax
参数赋给 寄存器
system_call
其他

函数原型:

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <unistd.h>
#include <sys/syscall.h>   /* For SYS_xxx definitions */long syscall(long number, ...);

使用示例:

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <signal.h>int main(int argc, char *argv[])
{pid_t tid;tid = syscall(SYS_gettid);tid = syscall(SYS_tgkill, getpid(), tid, SIGHUP);
}

内存映射mmap

应用层和内核直接数据交互,可以通过映射内存块的方式。
函数原型:

 #include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);

使用示例:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int
main(int argc, char *argv[])
{char *addr;int fd;struct stat sb;off_t offset, pa_offset;size_t length;ssize_t s;if (argc < 3 || argc > 4) {fprintf(stderr, "%s file offset [length]\n", argv[0]);exit(EXIT_FAILURE);}fd = open(argv[1], O_RDONLY);if (fd == -1)handle_error("open");if (fstat(fd, &sb) == -1)           /* To obtain file size */handle_error("fstat");offset = atoi(argv[2]);pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);/* offset for mmap() must be page aligned */if (offset >= sb.st_size) {fprintf(stderr, "offset is past end of file\n");exit(EXIT_FAILURE);}if (argc == 4) {length = atoi(argv[3]);if (offset + length > sb.st_size)length = sb.st_size - offset;/* Can't display bytes past end of file */} else {    /* No length arg ==> display to end of file */length = sb.st_size - offset;}addr = mmap(NULL, length + offset - pa_offset, PROT_READ,MAP_PRIVATE, fd, pa_offset);if (addr == MAP_FAILED)handle_error("mmap");s = write(STDOUT_FILENO, addr + offset - pa_offset, length);if (s != length) {if (s == -1)handle_error("write");fprintf(stderr, "partial write");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);
}

SQ_RING、CQ_RING、SQES关系

SQ_RING和CQ_RIeNG的各项偏移指向SQES,真正存储数据的是SQES。

#mermaid-svg-9Xe8OxF6USfJw8PU {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-9Xe8OxF6USfJw8PU .error-icon{fill:#552222;}#mermaid-svg-9Xe8OxF6USfJw8PU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9Xe8OxF6USfJw8PU .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-9Xe8OxF6USfJw8PU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9Xe8OxF6USfJw8PU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9Xe8OxF6USfJw8PU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9Xe8OxF6USfJw8PU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9Xe8OxF6USfJw8PU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9Xe8OxF6USfJw8PU .marker.cross{stroke:#333333;}#mermaid-svg-9Xe8OxF6USfJw8PU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9Xe8OxF6USfJw8PU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9Xe8OxF6USfJw8PU .cluster-label text{fill:#333;}#mermaid-svg-9Xe8OxF6USfJw8PU .cluster-label span{color:#333;}#mermaid-svg-9Xe8OxF6USfJw8PU .label text,#mermaid-svg-9Xe8OxF6USfJw8PU span{fill:#333;color:#333;}#mermaid-svg-9Xe8OxF6USfJw8PU .node rect,#mermaid-svg-9Xe8OxF6USfJw8PU .node circle,#mermaid-svg-9Xe8OxF6USfJw8PU .node ellipse,#mermaid-svg-9Xe8OxF6USfJw8PU .node polygon,#mermaid-svg-9Xe8OxF6USfJw8PU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9Xe8OxF6USfJw8PU .node .label{text-align:center;}#mermaid-svg-9Xe8OxF6USfJw8PU .node.clickable{cursor:pointer;}#mermaid-svg-9Xe8OxF6USfJw8PU .arrowheadPath{fill:#333333;}#mermaid-svg-9Xe8OxF6USfJw8PU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9Xe8OxF6USfJw8PU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9Xe8OxF6USfJw8PU .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-9Xe8OxF6USfJw8PU .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-9Xe8OxF6USfJw8PU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9Xe8OxF6USfJw8PU .cluster text{fill:#333;}#mermaid-svg-9Xe8OxF6USfJw8PU .cluster span{color:#333;}#mermaid-svg-9Xe8OxF6USfJw8PU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9Xe8OxF6USfJw8PU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

memory
SQ_RING
...
item0
item1
item2
SQES
memory block
save data
CQ_RING
...
item0
item1

代码实现示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <linux/fs.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>#include <linux/io_uring.h>#define URING_QUEUE_DEPTH 1024
#define BLOCK_SZ            1024// 定义结构体,将内核数据mmap到用户层
struct app_io_sq_ring {unsigned *head;unsigned *tail;unsigned *ring_mask;unsigned *ring_entries;unsigned *flags;unsigned *array;
};struct app_io_cq_ring {unsigned *head;unsigned *tail;unsigned *ring_mask;unsigned *ring_entries;struct io_uring_cqe *cqes;
};// 返回
struct submitter {int ring_fd;struct app_io_sq_ring sq_ring;struct app_io_cq_ring cq_ring;struct io_uring_sqe *sqes;
};// file info
struct file_info {off_t file_sz;struct iovec iovecs[];
};int io_uring_setup(unsigned entries, struct io_uring_params *p)
{// io_uring.c// SYSCALL_DEFINE2(io_uring_setup,u32,entries,struct io_uring_params __user *,params)// 内核代码 io_uring_setupreturn (int)syscall(__NR_io_uring_setup, entries, p);}int io_uring_enter(int ring_fd, unsigned int to_submit,unsigned int min_complete, unsigned int flags)
{return (int)syscall(__NR_io_uring_enter, ring_fd, to_submit, min_complete, flags, NULL, 0);
}int app_setup_uring(struct submitter *s)
{struct io_uring_params p;memset(&p, 0, sizeof(p));// 创建 CQ和SQ 内存空间s->ring_fd = io_uring_setup(URING_QUEUE_DEPTH, &p);if (s->ring_fd < 0)return -1;int sring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned);int cring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe);// 单映射,判断cq和sq是否公用一块内存if (p.features & IORING_FEAT_SINGLE_MMAP){if (cring_sz > sring_sz)sring_sz = cring_sz;cring_sz = sring_sz;}// mmap sq空间void *sq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_SQ_RING);if (sq_ptr == MAP_FAILED)return -1;// 单映射,判断cq和sq是否公用一块内存void *cq_ptr = NULL;if (p.features & IORING_FEAT_SINGLE_MMAP){cq_ptr = sq_ptr;}else{// mmap cq空间cq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_CQ_RING);if (cq_ptr == MAP_FAILED)return -1;}struct app_io_sq_ring *sring = &s->sq_ring;struct app_io_cq_ring *cring = &s->cq_ring;// sq赋值sring->head = sq_ptr + p.sq_off.head;sring->tail = sq_ptr + p.sq_off.tail;sring->ring_mask= sq_ptr + p.sq_off.ring_mask;sring->ring_entries = sq_ptr + p.sq_off.ring_entries;sring->flags = sq_ptr + p.sq_off.flags;sring->array = sq_ptr + p.sq_off.array;// 将sqes映射到用户空间s->sqes = mmap(0, p.sq_entries * sizeof(struct io_uring_sqe),PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_SQES);if (s->sqes == MAP_FAILED)return -1;// cq赋值cring->head = cq_ptr + p.cq_off.head;cring->tail = cq_ptr + p.cq_off.tail;cring->ring_mask = cq_ptr + p.cq_off.ring_mask;cring->ring_entries = cq_ptr + p.cq_off.ring_entries;cring->cqes = sq_ptr + p.cq_off.cqes;return 0;
}// get file size
off_t get_file_size(int fd)
{struct stat st;if (fstat(fd, &st) < 0)return -1;if (S_ISBLK(st.st_mode)){unsigned long long bytes;if (ioctl(fd, BLKGETSIZE64, &bytes) != 0)return -1;return bytes;}else if (S_ISREG(st.st_mode))return st.st_size;return -1;
}void output_to_console(char *buf,int len)
{while (len--){fputc(*buf++, stdout);}
}void read_from_cq(struct submitter *s)
{struct file_info *fi;struct app_io_cq_ring *cring = &s->cq_ring;struct io_uring_cqe *cqe;unsigned head = *cring->head;while (1){if (head == *cring->tail)break;cqe = &cring->cqes[head&*s->cq_ring.ring_mask];fi = (struct file_info*)cqe->user_data;if (cqe->res < 0)fprintf(stderr, "error: %d\n", cqe->res);int blocks = fi->file_sz / BLOCK_SZ;if (fi->file_sz%BLOCK_SZ)blocks++;int i = 0;while (++i < blocks){output_to_console(fi->iovecs[i].iov_base, fi->iovecs[i].iov_len);printf("################################ i : %d, blocks : %d\n",i, blocks);}head++;printf("head : %d,tail : %d, blocks: %d\n", head, *cring->tail,blocks);}*cring->head = head;printf("read form cq end!\n");
}int submit_to_sq(char *file_path,struct submitter *s)
{int filefd = open(file_path, O_RDONLY);if (filefd < 0)return -1;off_t filesz = get_file_size(filefd);if (filesz < 0) {close(filefd);return -1;}struct app_io_sq_ring *sring = &s->sq_ring;off_t bytes_remaining = filesz;int blocks = filesz / BLOCK_SZ;if (filesz%BLOCK_SZ)blocks++;struct file_info *fi = malloc(sizeof(struct file_info) + sizeof(struct iovec)*blocks);if (!fi){close(filefd);return -2;}fi->file_sz = filesz;unsigned current_block=0;while (bytes_remaining){off_t bytes_to_read = bytes_remaining;if (bytes_to_read > BLOCK_SZ)bytes_to_read = BLOCK_SZ;fi->iovecs[current_block].iov_len = bytes_to_read;void *buf;if (posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ)){close(filefd);return 1;}fi->iovecs[current_block].iov_base = buf;current_block++;bytes_remaining -= bytes_to_read;}unsigned next_tail = 0, tail = 0, index = 0;next_tail = tail = *sring->tail;next_tail++;index = tail &*s->sq_ring.ring_mask;struct io_uring_sqe *sqe = &s->sqes[index];sqe->fd = filefd;sqe->flags = 0;sqe->opcode = IORING_OP_READV;sqe->addr = (unsigned long)fi->iovecs;sqe->len = blocks;sqe->off = 0;sqe->user_data = (unsigned long long)fi;sring->array[index] = index;tail = next_tail;if (*sring->tail != tail)*sring->tail = tail;int ret = io_uring_enter(s->ring_fd, 1, 1, IORING_ENTER_GETEVENTS);if (ret < 0){close(filefd);return -1;}close(filefd);return 0;}int main(int argc, char *argv[])
{struct submitter *s = malloc(sizeof(struct submitter));if (s == NULL){perror("malloc fail");return -1;}memset(s, 0, sizeof(struct submitter));if (app_setup_uring(s))return 1;int i = 1;for (i = 1; i < argc; i++){if (submit_to_sq(argv[i], s))return 1;read_from_cq(s);}return 0;
}

总结

io_uring比epoll好的点是io_uring使用共享内存,不仅仅共享了IO事件,需要的变量也通过共享内存共享到用户空间,像SQ和CQ队列。io_uring不仅可以处理网络IO,也可以处理磁盘IO。
封装io_uring处理磁盘IO事件:
(1)io_uring_setup,创建SQ和CQ以及SQES,mmap io_uring_cqe / io_uring_sqe / SQES到用户空间。
(2)submit 文件IO到SQ
(3)从CQ中读取文件IO

后言

本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux系统提升感兴趣的读者,可以点击链接,详细查看详细的服务:C/C++服务器课程

Linux网络设计之实现io_uring用户态接口相关推荐

  1. Linux网络设计之用户态协议栈与dpdk

    用户态协议栈设计与dpdk dpdk环境开启 Windowe下配置静态IP表 DPDK API介绍 struct rte_memzone结构体 struct rte_mempool结构体 struct ...

  2. 【linux】Linux kernel uapi header file(用户态头文件)

    uapi目录的创建原因 Linux在3.7以后把很多header file移到 include/uapi或是arch/xxxx/include/uapi下,为了解决include recursive( ...

  3. Linux内核笔记--内存管理之用户态进程内存分配

    内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...

  4. Linux内核协议栈-一个socket的调用过程,从用户态接口到底层硬件

    用户创建socket 调用内核__sock_create int __sock_create(struct net *net, int family, int type, int protocol,s ...

  5. java运行在用户态_理解Linux用户态和内核态

    Linux整体架构图 我们先来看一张Linux整体架构图. 系统调用 ​ 系统调用时操作系统的最小功能单位.根据不同的应用场景,不同的Linux发行版本提供的系统调用数量也不尽相同,大致在240-35 ...

  6. 【转】linux内核态和用户态的区别

    原文网址:http://www.mike.org.cn/articles/linux-kernel-mode-and-user-mode-distinction/ 内核态与用户态是操作系统的两种运行级 ...

  7. 用户态 tcpdump 如何实现抓到内核网络包的?

    用户态 tcpdump 如何实现抓到内核网络包的?运行在用户态的程序 tcpdump 是如何实现抓到内核态的包的呢?https://mp.weixin.qq.com/s/ZX8Jluh-RgJXcVh ...

  8. 用户态线程库原理、设计与实现

    目录 一.概述 二.开发环境 三. 相关原理及算法 3.1  不得不知道的知识--c语言的函数参数传递机制 3.1.1 从汇编的角度理解C语言函数传参的方式 3.1.2 函数调用过程的堆栈变化 3.2 ...

  9. 浅谈Linux用户态和内核态

    为什么要分用户态和内核态? 在 CPU 的所有指令中,有一些指令是非常危险的,如果错用,将导致整个系统崩溃.比如:清内存.设置时钟等.如果所有的程序都能使用这些指令,那么你的系统一天死机 n 回就不足 ...

最新文章

  1. 一步一步实现自己的模拟控件(9)——消息处理
  2. ognl 表达式常用表达式语言
  3. linux shell脚本写法,linux: 常用shell脚本写法
  4. 37.rust属性.txt
  5. 云小课 | 不小心删除了数据库,除了跑路还能咋办?
  6. android开发 视图联动_Flutter混合APP开发
  7. 【Python3 爬虫】13_爬取博客园首页所有文章
  8. 社交系统ThinkSNS+版本的 SPA(H5)安装教程
  9. Java必备技能:IDEA一定要懂的32条快捷键
  10. python中的opencv读取数字_用python和OpenCV从图像中提取数字
  11. 【软考-中级】系统集成项目管理工程师-【2信息系统集成和服务管理】
  12. matlab .opj,HRTF 3D 音效 Matlab实现
  13. Centos 8 搭建samba文件共享服务(超详细)
  14. swap 内存交换原理
  15. Python入门干货经验(免费提供资料)
  16. linux echo完全删除文件,LInux上清空或删除文件的5中方法
  17. C#实现帮助文档CHM
  18. 移动网络运营商的大数据
  19. 【华为机试真题详解】高矮个子排队
  20. 为什么专用服务器要选择至强Xeon系列的处理器呢?

热门文章

  1. 简述关系数据库的数据完整性规则_认识关系数据库的完整性规则
  2. 一种根据EI检索结果对比CCF期刊/会议评级的程序
  3. 2021-10-27 求方向盘转角
  4. 乔布斯在斯坦福大学的演讲(三)[转载]
  5. pytorch——卷积神经网络
  6. 刻薄是因为底子薄,尖酸是因为心里酸
  7. 练习:数字时钟(Python 自定义类)
  8. 机器学习数据预处理之离群值/异常值:标准差法
  9. Delphi 把字符串复制到剪贴板
  10. cocos2dx之音效引擎