Libev - обратные вызовы ввода-вывода

У меня есть чат-сервер в C / Linux, использующий сокеты TCP. При использовании libev я могу создать наблюдатель ev_io для событий чтения один раз для сокета. Что-то вроде:

ev_io* new_watcher = (ev_io*)malloc(sizeof(ev_io));

//initialize the watcher
ev_init(new_watcher, read_cb);

//set the fd and event to fire on write
ev_io_set(new_watcher, watcher->fd, EV_READ);

//start watching
ev_io_start(loop, new_watcher);

и это отлично работает, потому что событие чтения срабатывает только тогда, когда есть данные для чтения. Однако я должен относиться к событиям записи по-другому, потому что они постоянно срабатывают, даже когда у меня нет данных для записи. Чтобы решить эту проблему, мой read_callback создает наблюдателя ev_io для данных записи только тогда, когда есть данные, готовые к записи, а затем write_callback удаляет наблюдателя после того, как он отправил свое сообщение.

Это означает, что я выделяю, инициализирую, устанавливаю, наблюдаю, снимаю отслеживание и освобождаю наблюдателя записи каждый раз, когда мне нужно обработать сообщение. Боюсь, что могу с этим справиться неправильно и неэффективно.

Как лучше всего обрабатывать события write_callback в libev?

Заранее спасибо.


person Josh Brittain    schedule 26.02.2012    source источник


Ответы (3)


Распределение может добавить некоторые накладные расходы, вы можете использовать статическую переменную вместо malloc или malloc один раз и освободить ее только после завершения цикла событий. Вам нужно только установить перед записью и сбросить после успешного завершения. Но да, вот как это нужно делать.

person b0ti    schedule 29.02.2012

Легко, есть также ev_io_stop, поэтому вы не запускаете наблюдатель записи, если вам нечего писать, а внутри обратного вызова вы вызываете ev_io_stop, когда вы записали весь буфер.

В общем случае, когда вы редко переполняете буфер записи (потому что ваши данные малы и вы не пишете слишком часто), вы можете сделать это более эффективным, пытаясь записать данные напрямую (если наблюдатель не активен) и только буферизация данных и запуск наблюдателя записи, если вы не можете полностью записать их.

В соответствии с приведенными выше предположениями это означает, что вам почти никогда не нужно запускать средство наблюдения за записью. Недостатком является значительно более сложный код, поэтому во многих случаях лучше начинать с простой логики «добавить данные для записи в буфер, запустить наблюдатель, внутри наблюдателя остановить его, если буфер был полностью записан».

person Remember Monica    schedule 22.12.2012

Способ, которым я решил эту ситуацию, заключался в том, чтобы иметь функцию для записи данных, которая принимает указатель на буфер и длину. Он сохраняет указатель и длину в структуре данных очереди и включает событие записи.

Когда срабатывает обратный вызов события записи, он проверяет очередь записи, чтобы увидеть, есть ли какие-либо ожидающие записи. Если они есть, он берет следующую ожидающую запись в очереди и записывает ее в файловый дескриптор. Затем непосредственно перед завершением обратного вызова записи он проверяет, не пуста ли ожидающая очередь записи. Если да, то он отключает событие записи.

Если вы сделаете свои объекты событий чтения / записи глобальными переменными, то они будут выделены и освобождены только один раз. Вы включаете событие записи, когда знаете, что есть данные для записи, и отключаете его, когда больше нет данных для записи.

Мой код немного сложнее, чем приведенное выше описание, но я опубликую здесь ссылку, чтобы вы могли посмотреть. Код, о котором я говорю конкретно, находится в файлах aiofd.h и aiofd.c (aiofd == файловый дескриптор асинхронного ввода-вывода): https://bitbucket.org/wookie/cutil/

Надеюсь, это поможет.

person Dave Huseby    schedule 10.07.2012