无法在内核 5.4 中使用 io_uring 写入文件(始终返回 EINVAL)

Unable to write file with io_uring in kernel 5.4 (always returns EINVAL)

提问人:Chris Z 提问时间:11/18/2023 最后编辑:Chris Z 更新时间:11/18/2023 访问量:62

问:

我正在尝试制作一个简单的程序,以在x86_64平台上使用内核 5.4.0-150 和 Ubuntu Bionic (18.04) 写入文件。无论我做什么,cqe->res 总是在 EINFAL 中失败。我不知道什么参数可能是错误的。

用内核 5.4 编写 SQE 的工作吗?(这台机器运行的是 5.4,因为它被宣传为 LTS 内核)。

#include <stdio.h>
#include <liburing.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

void dump_sqe(struct io_uring_sqe *sqe);

int main() {
    // Initialize the io_uring structure and create a ring buffer and completion events
    struct io_uring ring;
    void* buf;
    int fd;
    
    printf("Using liburing %d.%d\n",IO_URING_VERSION_MAJOR,IO_URING_VERSION_MINOR);
    
    int ret = io_uring_queue_init(16, &ring, 0);
    if (ret < 0) {
      printf("io_uring_queue_init: %d\n",ret);
    }

    if((fd = open("./temp.txt", O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0)
       perror("open");

    // Set up the write operation
    struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);

    posix_memalign(&buf,4096,4096);
    strcpy(buf,"Hello, world!\n");

    io_uring_prep_write(sqe, fd, buf, strlen(buf), 0);
    dump_sqe(sqe);
    
    // Submit the write request to the kernel
    ret = io_uring_submit(&ring);
    if (ret < 0) {
      printf("io_uring_submit: %d\n",ret);
    }

    // Wait for the completion of the write request
    struct io_uring_cqe *cqe;
    ret = io_uring_wait_cqe(&ring, &cqe);
    if (ret < 0) {
      printf("io_uring_wait_cqe: %d\n",ret);
    }

    // Process the completed write request
    if (cqe->res < 0) {
      printf("cqe->res = %d\n",cqe->res);
    } else {
      printf("Success\n");
    }

    // Clean up
    io_uring_cqe_seen(&ring, cqe);
    io_uring_queue_exit(&ring);
    return 0;
}

void dump_sqe(struct io_uring_sqe *sqe)
{
  printf("sqe->opcode = 0x%02x\n"
     "sqe->flags = 0x%08x\n"
     "sqe->ioprio = %d\n"
     "sqe->fd = %d\n"
     "sqe->off = %lld\n"
     "sqe->addr = 0x%016llu\n"
     "sqe->len = %d\n"
     "sqe->rw_flags = %d\n"
     "sqe->buf_index = %d\n"
     "sqe->personality = %d\n"
     "sqe->file_index = %d\n"
     "sqe->addr3 = %lld\n"
     "sqe->__pad2[0] = %lld\n\n",
     sqe->opcode,
     sqe->flags,
     sqe->ioprio,
     sqe->fd,
     sqe->off,
     sqe->addr,
     sqe->len,
     sqe->rw_flags,
     sqe->buf_index,
     sqe->personality,
     sqe->file_index,
     sqe->addr3,
     sqe->__pad2[0]);
}

运行上述代码将生成以下内容:

Using liburing 2.4
sqe->opcode = 0x17
sqe->flags = 0x00000000
sqe->ioprio = 0
sqe->fd = 4
sqe->off = 0
sqe->addr = 0x0094870066995200
sqe->len = 14
sqe->rw_flags = 0
sqe->buf_index = 0
sqe->personality = 0
sqe->file_index = 0
sqe->addr3 = 0
sqe->__pad2[0] = 0

cqe->res = -22

我是否做错了什么,或者是否有特定的内核版本可以开始写入工作?我使用了页面对齐的缓冲区,认为这可能是问题所在,但这没有任何区别。

任何人都可以建议尝试其他方法吗?不幸的是,由于组织策略限制,我无法升级这台机器上的内核。

C Linux Linux-内核 IO-URING

评论

0赞 Chris Z 11/18/2023
我把 buf 设为空*,这样它就会被posix_memalign接受。这需要强制转换到 strcpy,以避免在编译时出现 gcc 警告。
0赞 Chris Z 11/18/2023
你是对的。我刚刚检查过,如果我将 void* 传递给 strcpy,gcc 不会报告错误。它不会改变我遇到的问题,但这是一个有用的事实。我将更新示例以将其删除。
0赞 chux - Reinstate Monica 11/18/2023
深入研究它的原因是确保你做的是 C 编译,而不是其他语言。

答:

1赞 Craig Estey 11/18/2023 #1

快速浏览...

您正在使用 .这由以下方式显示:STDOUT_FILENOsqe->fd = 1

发件人: https://man.archlinux.org/man/io_uring_setup.2.en

它[似乎]指出 fd 必须打开和/或必须是存储设备。O_DIRECT

所以,我认为你不能使用.stdout

为了进行测试[至少],请尝试打开文件或设备。/dev

评论

0赞 Chris Z 11/18/2023
谢谢。我在常规文件上尝试了完全相同的代码,得到了相同的结果。我认为显示 STDOUT 对每个人来说都会更容易,但我将编辑示例以显示常规文件。
0赞 Craig Estey 11/18/2023
@ChrisZ 通过更新,为了进一步消除某些可能性,您可以尝试: 或者用 写入一些数据。我的内核中没有 ioring,所以我实际上无法自己测试任何东西。有一堆归零的字段。他们中的任何一个都需要一些“真实”的价值(例如)吗?ftruncate(fd,4096)writerw_flags
0赞 Chris Z 11/18/2023
如果我在打开后添加,它会将文件扩展为长度为 4096。但是,io_uring写入仍然失败,cqe->res = EINVAL,并且文本永远不会写入磁盘。我不知道是否需要设置任何rw_flags。我想这就是我问这个问题的原因。老实说,我不知道我是不是做错了什么。ftruncate(fd,4096);
0赞 Craig Estey 11/18/2023
看看 github.com/axboe/liburing/tree/master/test 中的测试,github.com/axboe/liburing/tree/master/examples 我注意到的一件事是,在做添加?一个可以尝试的简单示例:github.com/axboe/liburing/blob/master/examples/link-cp.copenO_DIRECT
0赞 Chris Z 11/18/2023
于是剧情变厚了。如果我先进行 SQE 读取,然后后续的 SQE 写入成功。但是,如果不执行读取操作,写入 SQE 将失败并显示 EINVAL。这是 5.4 内核中的错误,还是有原因的?
2赞 Chris Z 11/18/2023 #2

我找到了这个问题的答案:

io_uringopcode,直到 5.6 版才出现在 Linux 内核中。IORING_OP_READ (0x16)IORING_OP_WRITE (0x17)

要在版本 5.4 中使用,您只能使用: ,并且长度必须是 512 字节的整数倍。io_uringio_uring_prep_readv/io_uring_prep_writev

谢谢大家的帮助。