Я записываю некоторые данные на диск с помощью io_uring и заметил, что смещение файла не увеличивается автоматически после выдачи запроса на запись. Таким образом, если я отправлю два запроса на запись через liburing, второй перезапишет первый, поскольку оба пытаются записать в начало файла. Использование этих posix API по отдельности (write и writev) не вызывает никаких проблем, но использование их через liburing никогда не приводит к смещению файла вперед. На странице руководства для liburing говорится, что установка offset в -1 приведет к автоматическому увеличению смещения файла, но, похоже, это не так.
Небольшой пример ниже. Ожидаемое поведение заключается в том, что в файл записывается 4096 байт чисел от 0 до 1023, за которыми следуют 4096 байт нулей. Однако файл содержит только 4096 байт нулей. Если я удалю строку write_buffer_to_file(2), она теперь будет содержать 4096 байт чисел, поэтому кажется, что второй вызов перезаписывает содержимое первого. Тот факт, что lseek всегда возвращает 0, подтверждает, что смещение файла никогда не меняется.
Фрагмент кода скомпилирован с помощью gcc и запущен на RHEL 9.3 с ядром 5.14.
#include <cstring>
#include <iostream>
#include <liburing.h>
#include <unistd.h>
// a small macro to check for errors
#define SYSCALL(expr) if ((expr) < 0) { \
perror("System call error"); \
}
const int WRITE_SIZE = 4096; // satisfy alignment requirement of O_DIRECT
int fd; // file descriptor
int *buffer; // write buffer
struct io_uring ring;
// write the content of the buffer to fd; the data argument sets user_data in the sqe, which shouldn't affect the result
void write_buffer_to_file(int data) {
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_sqe_set_data(sqe, (void*)(intptr_t)data);
// according to the documentation, setting offset to -1 will advance the offset
// neither 0 nor -1 work in my testing
io_uring_prep_write(sqe, fd, buffer, WRITE_SIZE, -1);
SYSCALL(io_uring_submit(&ring))
std::cout << "Submitted " << sqe->user_data << std::endl;
// now wait for it to complete
struct io_uring_cqe *cqe;
SYSCALL(io_uring_wait_cqe(&ring, &cqe));
if (cqe->res < 0) {
perror("cqe res less than 0");
std::cerr << std::strerror(-cqe->res) << std::endl;
}
io_uring_cqe_seen(&ring, cqe);
std::cout << "Reaped " << io_uring_cqe_get_data(cqe) << std::endl;
// this line always prints 0 even though it's supposed to advanced 4096 bytes
std::cout << "Current offset: " << lseek(fd, 0, SEEK_CUR) << std::endl;
}
int main() {
// set up the file and the write buffer
fd = open("test_file", O_CREAT | O_WRONLY | O_DIRECT, 0744);
SYSCALL(fd);
// O_DIRECT has stricter memory alignment requirements
posix_memalign((void**)&buffer, 512, WRITE_SIZE);
for (int i = 0; i < WRITE_SIZE / sizeof(int); i++) {
buffer[i] = i;
}
io_uring_queue_init(5, &ring, 0);
write_buffer_to_file(1);
// set everything in the buffer to 0 and then write again
memset(buffer, 0, WRITE_SIZE);
write_buffer_to_file(2);
io_uring_queue_exit(&ring);
close(fd);
return 0;
}





Оказывается, это проблема старой версии ядра. Использование того же кода в Ubuntu 22.04 (ядро 6.2) не приводит к каким-либо проблемам.
См. этот выпуск GitHub. Было бы неплохо указать коммит, исправляющий эту ошибку, но исправление произошло очень давно, поэтому его трудно найти.