从linux内核看io_uring的实现( 二 )


从linux内核看io_uring的实现

文章插图
 
下面我们看struct_size是如何计算的 。
#define struct_size(p, member, count)__ab_c_size(count,sizeof(*(p)->member) + __must_be_array((p)->member),sizeof(*(p)))static inline __must_check size_t __ab_c_size(size_t a, size_t b, size_t c){ size_t bytes; // 计算a * b保存到bytes if (check_mul_overflow(a, b, &bytes))return SIZE_MAX; // 计算bytes + c保存搭配bytes if (check_add_overflow(bytes, c, &bytes))return SIZE_MAX; return bytes;}我们看到计算方式就是数组元素大小*元素个数+结构体本身的大小 。计算完结构体大小后又通过array_size计算了另一个数组的大小并加起来,所以io_rings的结构体如下所示 。
从linux内核看io_uring的实现

文章插图
 
分配了io_rings之后我们继续看接下来的逻辑 。
static int io_allocate_scq_urings(struct io_ring_ctx *ctx,struct io_uring_params *p){// ...// 记录到ctx中ctx->rings = rings;// sq_array记录rings结构体中,u32数组的首地址ctx->sq_array = (u32 *)((char *)rings + sq_array_offset);// 用于回环处理rings->sq_ring_mask = p->sq_entries - 1;rings->cq_ring_mask = p->cq_entries - 1;// 队列长度rings->sq_ring_entries = p->sq_entries;rings->cq_ring_entries = p->cq_entries;ctx->sq_mask = rings->sq_ring_mask;ctx->cq_mask = rings->cq_ring_mask;// 请求队列的数组大小size = array_size(sizeof(struct io_uring_sqe), p->sq_entries);// 分配内存并记录到sq_sqesctx->sq_sqes = io_mem_alloc(size);return 0;}进行了一系列设置后,架构如下 。
从linux内核看io_uring的实现

文章插图
 
创建完io_rings结构体后,我们继续回到io_uring_create中 。
相关视频推荐
io_uring 新起之秀的linux io模式,是如何媲美epoll的
linux下的epoll实战揭秘——支撑亿级IO的底层基石
学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂
需要C/C++ linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
从linux内核看io_uring的实现

文章插图
【从linux内核看io_uring的实现】 
2 设置io_uring_params内核申请完系列结构体后,需要通过io_uring_params结构体返回给调用方 。
static int io_uring_create(unsigned entries, struct io_uring_params *p,struct io_uring_params __user *params) {ret = io_allocate_scq_urings(ctx, p);// 初始化poll模式相关逻辑,如果开启了的话ret = io_sq_offload_start(ctx, p);memset(&p->sq_off, 0, sizeof(p->sq_off));// 记录字段在结构体的偏移p->sq_off.head = offsetof(struct io_rings, sq.head);p->sq_off.tail = offsetof(struct io_rings, sq.tail);p->sq_off.ring_mask = offsetof(struct io_rings, sq_ring_mask);p->sq_off.ring_entries = offsetof(struct io_rings, sq_ring_entries);p->sq_off.flags = offsetof(struct io_rings, sq_flags);p->sq_off.dropped = offsetof(struct io_rings, sq_dropped);p->sq_off.array = (char *)ctx->sq_array - (char *)ctx->rings;memset(&p->cq_off, 0, sizeof(p->cq_off));p->cq_off.head = offsetof(struct io_rings, cq.head);p->cq_off.tail = offsetof(struct io_rings, cq.tail);p->cq_off.ring_mask = offsetof(struct io_rings, cq_ring_mask);p->cq_off.ring_entries = offsetof(struct io_rings, cq_ring_entries);p->cq_off.overflow = offsetof(struct io_rings, cq_overflow);p->cq_off.cqes = offsetof(struct io_rings, cqes);p->cq_off.flags = offsetof(struct io_rings, cq_flags);// 内核支持的属性p->features = IORING_FEAT_SINGLE_MMAP | IORING_FEAT_NODROP |IORING_FEAT_SUBMIT_STABLE | IORING_FEAT_RW_CUR_POS |IORING_FEAT_CUR_PERSONALITY | IORING_FEAT_FAST_POLL |IORING_FEAT_POLL_32BITS;copy_to_user(params, p, sizeof(*p))// 获取fdret = io_uring_get_fd(ctx);return ret;}io_uring_create继续进行了一系列赋值,赋值完后架构如下 。
从linux内核看io_uring的实现

文章插图
 
3 获取文件描述符内核通过io_uring_get_fd获取文件描述符返回给调用方 。
static int io_uring_get_fd(struct io_ring_ctx *ctx){struct file *file;// 获取一个可用fdint ret = get_unused_fd_flags(O_RDWR | O_CLOEXEC);// 分配一个file结构体,设置函数集为io_uring_fops,并关联上下文ctxfile = anon_inode_getfile("[io_uring]", &io_uring_fops, ctx,O_RDWR | O_CLOEXEC);// 关联fd和file结构体fd_install(ret, file);return ret;}io_uring_get_fd申请了一个fd和file,这是遵循vfs的设计,最重要的是把io_uring的函数集挂在到file上,后续通过fd操作的io_uring实例的时候,经过vfs后就会执行对应的函数,另外还需要把ctx和file关联起来,因为后续通过fd操作io_uring时,需要拿到fd对应的io_uring上下文 。至此 。


推荐阅读