最近的project上遇到了需要在多个进程共享一个io-uring,对同一个文件进行读写的需求,这边写一下实现思路和遇到的问题。
实现思路:
首先io-uring的内存空间是内核态和用户态是共享的,接着最新一版io-uring提供了io_uring_queue_init_mem函数,通过这个函数原本由内核分配的内存空间现在可以由用户态程序提供(一个内存大页)。在此基础上,用户态提供的内存是共享的,也就可以实现多个进程之间的共享。
下面直接上代码:
用户态程序首先构建一个共享的内存大页
bs->sh_fd = shmget(1234, SHM_HUGE_2M, IPC_CREAT|SHM_HUGE_2M|SHM_HUGETLB|0666);
if (bs->sh_fd == -1 )
{
printf("semget is wrong \n");
}
bs->s_ptr = shmat(bs->sh_fd, bs->s_ptr, 0);
if (bs->s_ptr == (void *)-1)
{
printf("semget is wrong \n");
}
memset(bs->s_ptr, 0, SHM_HUGE_2M);
这边我使用了系统默认的2M大页,但是感觉会有sqe,和cqe上的限制,返回值显示只用了 8K的内存,但是entry的数量最大设置到64。(这块的内核代码没有细看到底问题出在哪,因为64个sqe对我来说也够用了,也许分配个1G的大页也许会解决)
bs->p->flags |= IORING_SETUP_SQPOLL | IORING_SETUP_NO_MMAP;
bs->p->resv[0] = bs->p->resv[1] = bs->p->resv[2] = 0;
bs->p->sq_thread_idle = 0xfffffffe;
ret = io_uring_queue_init_mem(64, bs->ring, bs->p, bs->s_ptr, SHM_HUGE_2M);
printf("ret is %u\n",ret);
if (ret== -ENOMEM) {
printf("io_uring share memory mmap init error. no enought memory\n");
}
if (ret == -EPERM && geteuid()) {
fprintf(stdout, "SQPOLL skipped for regular user\n");
goto err_no_bs_opts;
}
另外一个进程,在打开共享大页时需要使用和主进程使用相同的地址,这是因为struct io_uring这个结构体里的指针都是按照偏移量固定好的,如果shmget使用系统自动分配的地址,会导致获得到的结构体里的指针不在当前进程的内存空间中,造成segement fault。
trace_fd = shmget((key_t)1234, size, 0666);
if (trace_fd == -1)
{
printf("shmget error\n");
}
buf = shmat(trace_fd, addr1, 0);
if (buf == (void *)-1)
{
printf("shmat error\n");
}
后面将buf转化为struct io_uring*,就可以愉快的使用io-uring了。