Правильная обработка контекстных данных в обратных вызовах libaio? - PullRequest
3 голосов
/ 22 марта 2012

Я работаю с асинхронным вводом-выводом на уровне ядра (т.е. libaio.h). Перед отправкой struct iocb с использованием io_submit я установил обратный вызов с помощью io_set_callback, который вставляет указатель функции в iocb->data. Наконец, я получаю завершенные события, используя io_getevents и запускаю каждый обратный вызов.

Я хотел бы иметь возможность использовать некоторую контекстную информацию в обратном вызове (например, метку времени отправки). Единственный способ, с помощью которого я могу думать об этом, - это продолжать использовать io_getevents, но iocb->data указывает на структуру с контекстом и обратным вызовом.

Существуют ли другие способы сделать что-то подобное, и гарантированно ли будет iocb->data нетронутым при использовании io_getevents? Насколько я понимаю, есть еще один метод, с помощью которого libaio будет автоматически запускать обратные вызовы, что было бы проблемой, если бы iocb->data не указывал на функцию.

Любое разъяснение здесь было бы неплохо. Документация по libaio, похоже, действительно отсутствует.

1 Ответ

4 голосов
/ 23 марта 2012

Одно из решений, которое я бы назвал типичным, - это «извлечь» из iocb, а затем привести указатель, который вы вернули из io_getevents(), к вашей структуре.Примерно так:

struct my_iocb {
    iocb cb;
    void* userdata;
    // ... anything else
};

Когда вы выдаете свои задания, независимо от того, выполняете ли вы их по одному или в пакете, вы предоставляете массив структур указателей - iocb,это означает, что они также могут указывать на my_iocb.

Когда вы извлекаете уведомления из io_getevents(), вы просто приводите указатель io_event::obj к своему собственному типу:

io_event events[512];
int num_events = io_getevents(ioctx, 1, 512, events, NULL);
for (int i = 0; i < num_events; ++i) {
   my_iocb* job = (my_iocb*)events[i].obj;
   // .. do stuff with job
}

Если вы не хотите блокировать в io_getevents, а вместо этого получать уведомление через дескриптор файла (чтобы вы могли блокировать в select() или epoll(), что может быть более удобно), я бы порекомендовал использовать (без документов) eventfd интеграция.

Вы можете связать aiocb с дескриптором файла eventfd с помощью io_set_eventfd(iocb* cb, int fd).Всякий раз, когда задание завершается, он увеличивает eventfd на единицу.

Обратите внимание: если вы используете этот механизм, очень важно никогда не читать больше заданий из контекста io (с io_getevents()), чем счетчик eventfdсказал, что есть, в противном случае вы вводите условия гонки с того момента, когда вы читаете счетчик eventfd и пожинаете задания.

...