Как задать отложенное выполнение операции с помощью io_uring так, чтобы она была запущена через 3 секунды после вызова io_uring_submit?

Как правильно настроить параметры sQE в асинхронном TCP сервере с использованием liburing для создания задержки в 3 секунды после сабмита операции записи сообщения в файл и перед отправкой клиенту accept?
  • 20 апреля 2024 г. 11:20
Ответы на вопрос 2
Для задания отложенного выполнения операции с использованием io_uring можно использовать timerfd. Вот пример кода на C, который показывает, как это можно сделать:

```c
#include <stdio.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <string.h>
#include <sys/io_uring.h>
#include <stdlib.h>

int main() {
    int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
    if (timer_fd == -1) {
        perror("timerfd_create");
        exit(EXIT_FAILURE);
    }

    struct itimerspec ts;
    memset(&ts, 0, sizeof(ts));
    ts.it_value.tv_sec = 3; // установка таймера на 3 секунды
    if (timerfd_settime(timer_fd, 0, &ts, NULL) == -1) {
        perror("timerfd_settime");
        exit(EXIT_FAILURE);
    }

    struct io_uring ring;
    io_uring_queue_init(256, &ring, 0);

    // добавление события в io_uring
    struct io_uring_sqe* sqe = io_uring_get_sqe(&ring);
    io_uring_prep_poll_add(sqe, timer_fd, POLLIN);
    sqe->user_data = 1;

    io_uring_submit(&ring);

    // ждем перед выполнением операции
    struct io_uring_cqe* cqe;
    io_uring_wait_cqe(&ring, &cqe);
    printf("Delayed operation started\n");

    // выполнение операции

    close(timer_fd);
    io_uring_queue_exit(&ring);

    return 0;
}
```

Что касается настройки параметров sQE в асинхронном TCP сервере, то требуется установить правильные флаги для операции записи в файл и передачи данных клиенту. Есть несколько подходов к реализации этого с использованием liburing, однако обычно вы будете создавать SQE для каждой операции и отправлять их одновременно или в нужной последовательности.

Примерно, код мог бы выглядеть так:

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/io_uring.h>
#include <fcntl.h>

int main() {
    struct io_uring ring;
    io_uring_queue_init(256, &ring, 0);

    // настройка операции записи в файл
    struct io_uring_sqe* sqe1 = io_uring_get_sqe(&ring);
    io_uring_prep_write(sqe1, fd, buffer, size, offset);
    sqe1->flags = IOSQE_IO_DRAIN;
    sqe1->user_data = 1;

    // настройка операции отправки данных клиенту (например, с использованием sendmsg())
    struct io_uring_sqe* sqe2 = io_uring_get_sqe(&ring);
    io_uring_prep_sendmsg(sqe2, sockfd, msg, flags);
    sqe2->user_data = 2;

    io_uring_submit(&ring);

    // добавьте здесь задержку на 3 секунды

    // выполнение операции

    io_uring_queue_exit(&ring);

    return 0;
}
```

Здесь `fd` - это файловый дескриптор файла, `buffer` - буфер с данными, `size` - размер данных, `offset` - смещение для записи, `sockfd` - дескриптор сокета для отправки данных клиенту, `msg` - сообщение для отправки, `flags` - флаги для операции отправки данных.

Обратите внимание, что вы должны добавить задержку на 3 секунды между сабмитом операций записи и отправки данных клиенту. Для этого можете использовать подход, описанный в первом примере с использованием timerfd.
как выполнить задержку не понимаю

Для организации таймаута в liburing есть io_uring_prep_timeout , но кроме этого нужно сериализовать таймаут и все предыдущие команды (чтобы таймаут начинался после их окончания) и следующую после таймаута команду и сам таймаут (чтобы последующие команды не стартовали вместе с таймаутом). Сериализация делается установкой флага IOSQE_IO_DRAIN для sqe. У меня нарисовался следующий пример:
#include <fcntl.h>
#include <liburing.h>
#include <stdio.h>
#include <sys/mman.h>

#define QUEUE_DEPTH 16

int main(int argc, char **argv)
{
        int i;
        struct io_uring ring;
        struct io_uring_cqe *pcqe;
        static const char msg0[] = "before timeout\n";
        static const char msg1[] = "after timeout\n";
        struct io_uring_sqe *sqe;

        io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
        io_uring_prep_write(io_uring_get_sqe(&ring), 2, msg0, sizeof(msg0), 0);

        sqe = io_uring_get_sqe(&ring);
        io_uring_prep_timeout(sqe, (struct __kernel_timespec[]){{.tv_sec = 1}},
                              -1, IORING_TIMEOUT_ETIME_SUCCESS);
        io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);

        sqe = io_uring_get_sqe(&ring);
        io_uring_prep_write(sqe, 2, msg1, sizeof(msg1), 0);
        io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
        io_uring_submit(&ring);
        for (i = 0; i < 3; ++i) {
                int rv = io_uring_wait_cqe(&ring, &pcqe);
                if (rv < 0) {
                        errno = -rv;
                        perror("io_uring_wait_cqe");
                } else {
                        io_uring_cqe_seen(&ring, pcqe);
                }
        }
}
Похожие вопросы