Асинхронный ввод-вывод AIO с функцией обратного вызова — в поисках наилучшего способа

#c #asynchronous #signals #posix #aio

#c #асинхронный #сигналы #posix #aio

Вопрос:

Я пытаюсь изучить POSIX асинхронный ввод-вывод. Ниже приведена правка, которую я внес в чужой код иллюстрации. Я пытаюсь понять несколько вещей.

Во-первых, у меня есть цикл ожидания занятости ближе к концу, который завершает int read_complete . Является ли это «приемлемой» (безопасной, какой угодно, ….) альтернативой отключению возвращаемого значения aio_error() ? Кроме того, я думал, что в качестве альтернативы циклу ожидания занятости был бы способ перевести основной поток в спящий режим и заставить функцию обратного вызова отправить какой-то сигнал, который его разбудит. Но я не могу понять, как это сделать, если это можно сделать.

Наконец, я пытаюсь выяснить, как получить больше информации в функцию обратного i_am_done вызова. Например, допустим, я хотел поместить входные данные в буфер или разделить их между буферами, которые основной поток мог бы использовать позже, и буферы могли бы отличаться при каждом вызове, если бы мне нужно было выполнить несколько операций чтения. Как я мог бы сообщить i_am_done , что такое буферы?

 #include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <aio.h>
//#include <bits/stdc  .h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#include <errno.h>

const int BUFSIZE = 1024;

int read_complete = 0;


void i_am_done(sigval_t sigval)
{
    struct aiocb *req;

    req = (struct aiocb *)sigval.sival_ptr; //Pay attention here.
    /*Check again if the asynchrony is complete?*/
    if (aio_error(req) == 0)
    {
        read_complete = 1;

    }
    close(req->aio_fildes);


}



int main(void)
{
    struct aiocb my_aiocb;
    struct timeval t0, t1;


    int fd = open("file.txt", O_RDONLY);
    if (fd < 0)
        perror("open");
    bzero((char *)amp;my_aiocb, sizeof(my_aiocb));

    my_aiocb.aio_buf = malloc(BUFSIZE);
    if (!my_aiocb.aio_buf)
        perror("my_aiocb.aio_buf");

    my_aiocb.aio_fildes = fd;
    my_aiocb.aio_nbytes = BUFSIZE;
    my_aiocb.aio_offset = 0;

    //Fill in callback information
    /*
    Using SIGEV_THREAD to request a thread callback function as a notification method
    */
    my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
    my_aiocb.aio_sigevent.sigev_notify_function = i_am_done;
    my_aiocb.aio_sigevent.sigev_notify_attributes = NULL;

    my_aiocb.aio_sigevent.sigev_value.sival_ptr = amp;my_aiocb;

    int ret = aio_read(amp;my_aiocb);



    if (ret < 0)
        perror("aio_read");

    //The calling process continues to execute

    while (read_complete != 1) {}

    printf("main thread %sn", (char*)my_aiocb.aio_buf);
    return 0;
}
 

Комментарии:

1. wrt вопрос 2; что именно вы имеете в виду? вы эффективно «загружаете входные данные в буфер» прямо сейчас? Или вы имеете в виду, что внутри обратного вызова вы скопировали бы данные в другое место?

2. @vmt я имею в виду, что внутри обратного вызова я бы скопировал данные куда-нибудь еще. В другом примере вы можете видеть, что обратный вызов устанавливает глобальную переменную read_complete равной 1. Как я мог бы дать обратному вызову определенный указатель на какую-либо другую, возможно локальную, переменную, которую я хочу, чтобы она установила вместо этого? Возможно, например, разные вызовы обратного вызова должны приводить к настройке разных переменных.

Ответ №1:

Отвечая на вопрос № 2, просто определите структуру данных, в которой вы храните необходимые вам дополнительные данные, и установите sival_ptr для нее значение. Например:

 struct my_data {
    struct aiocb cb;

    // For demonstration's sake:
    int foo;
    char *bar;
    size_t quux;
}

// ...

struct my_data data;
data.cb.aio_sigevent.sigev_value.sival_ptr = amp;data; 
// Setup the rest of the struct and execute the read.
 

При обратном вызове теперь у вас есть доступ к структуре my_data.

Комментарии:

1. Так вот как это работает. Спасибо! Я удивлен скудостью ссылок на общую тему AIO. Знаете ли вы какие-нибудь хорошие из них? Я куплю книгу, если понадобится!

2. На справочной странице для aio есть пример, в котором вместо этого используется sigaction. Я не знаком с интерфейсом aio, однако вы могли бы заглянуть в библиотеки циклов событий, которые дают вам гораздо больше контроля и возможностей, чем интерфейс aio. на ум приходят libuv, libevent. Или же вы можете подумать о том, чтобы реализовать и свои собственные.

3. И я бы предположил, что нехватка ссылок обусловлена популярностью либо использования существующих реализаций библиотек циклов событий, либо относительной простотой их написания. И в libevent, и в libuv есть множество примеров, И если вам не нужны все функции (таймеры и т. Д.), Есть множество примеров простых циклов событий с использованием, например, epoll (в Linux), (p) select и т. Д.