Путаница с ключом порта завершения ввода-вывода

Я пишу сервер на основе порта завершения ввода-вывода (исходный код здесь) с помощью Windows DLL API в Python с использованием модуля ctypes. Но это довольно прямое использование API, и этот вопрос адресован тем, кто знаком с IOCP, а не с Python.

Насколько я понимаю в документации для CreateIoCompletionPort, вы указываете свой «определяемый пользователем» ключ завершения, когда вызываете эту функцию с дескриптором файла (в моем случае сокет), который вы связываете с созданным IOCP. Когда вы дойдете до вызова GetQueuedCompletionStatus, вы получите значение ключа завершения вместе с указателем на перекрывающийся объект. Ключ завершения должен идентифицировать, какой перекрывающийся объект и запрос были выполнены.

Однако предположим, что я передаю 100 в качестве ключа завершения в моем вызове CreateIoCompletionPort с перекрывающимся объектом. Когда тот же перекрывающийся объект завершает свой ввод-вывод и возвращается через GetQueuedCompletionStatus, ключ завершения, который сопровождает его, намного больше и не имеет никакого сходства с исходным значением 100.

Я неправильно понимаю, как работает ключ завершения, или я должен ошибаться в исходном коде, который я привел выше?


person Community    schedule 24.07.2009    source источник


Ответы (6)


В повседневной практике я обнаружил, что лучше просто сосредоточиться на OVERLAPPED результате, так как он не изменится. Один из способов его эффективного использования - это иметь что-то вроде следующего:

struct CompletionHandler
{
    OVERLAPPED dummy_ovl;
    /* Stuff that actually means something to you here */
};

Когда вы публикуете что-то в IOCP (через вызов ввода-вывода или просто сообщение через Win32 API), вы сначала создаете объект CompletionHandler, который будете использовать для отслеживания вызова, и приводите адрес этого объекта к OVERLAPPED*.

CompletionHander my_handler;
// Fill in whatever you need to in my_handler
// Don't forget to keep the original my_handler!

// I/O call goes here, and for OVERLAPPED* give: (OVERLAPPED*)&my_handler

Таким образом, когда вы получите результат OVERLAPPED, все, что вам нужно сделать, это вернуть его к CompletionHandler и вуаля! У вас есть исходный контекст вашего звонка.

OVERLAPPED* from_queued_completion_status;
// Actually get a value into from_queued_completion_status

CompletionHandler* handler_for_this_completion = (CompletionHandler*)from_queued_completion_status;
// Have fun!

Для получения дополнительных сведений о реальных настройках ознакомьтесь с реализацией ASIO для Windows в Boost (заголовок версии 1.42 здесь). Есть некоторые детали, такие как проверка указателя OVERLAPPED, который вы получаете от GetQueuedCompletionStatus, но, опять же, посмотрите ссылку, чтобы найти хороший способ реализации.

person Brian    schedule 24.02.2010

GetQueuedCompletionStatus возвращает две вещи: OVERLAPPED структуру и ключ завершения. Ключ завершения представляет информацию об устройстве, а структура OVERLAPPED представляет информацию о каждом вызове. Ключ завершения должен соответствовать тому, что было дано при вызове CreateIoCompletionPort. Обычно в качестве ключа завершения используется указатель на структуру, содержащую информацию о соединении.

Похоже, вы ничего не делаете с completionKey, возвращенным GetQueuedCompletionStatus.

Я предполагаю, что вы хотите:

if completionKey != acceptKey:
    Cleanup()
    ...

редактировать:

Знает ли Python каким-то образом, что структура OVERLAPPED, созданная в CreateAcceptSocket, используется Win32 API асинхронно и предотвращает ее сборку мусора?

person Tim Sylvester    schedule 24.07.2009
comment
Скажем, я передаю 100 в качестве ключа завершения в моем вызове CreateIoCompletionPort с перекрывающимся объектом. Когда для одного и того же перекрывающегося объекта завершен ввод-вывод и он возвращается через GetQueuedCompletionStatus, ключ завершения, который сопровождает его, намного больше и не имеет ничего общего с исходным значением 100. Из моего вопроса и почему я не сравниваю его в исходном коде . Вы, вероятно, правы насчет того, что перекрывающийся объект получает сборщик мусора, но это не имеет отношения к вопросу. - person ; 25.07.2009

Вы должны думать о ключе завершения как о данных «для каждого соединения» и о (расширенной) перекрывающейся структуре как о «операции ввода-вывода».

Некоторые люди используют расширенную перекрывающуюся структуру ОБЕИХ и хранят всю необходимую информацию в расширенной перекрывающейся структуре. Я всегда хранил объект с подсчетом ссылок, который заключает мой сокет в ключ завершения, и буфер данных с подсчетом ссылок в виде расширенной перекрывающейся структуры. Если вам интересно, вы можете увидеть пример кода IOCP на C ++ здесь .

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

person Len Holgate    schedule 20.10.2011

Проблема в том, как я передаю ключи завершения. Аргумент ключа завершения - это указатель, но он возвращает указатель, а не указанное значение - по крайней мере, меня это немного сбивает с толку.

Также ключ завершения, переданный для принятого перекрывающегося пакета соединения, является ключом прослушивающего сокета, а не принятым сокетом.

person Community    schedule 25.07.2009

Ключ завершения не является указателем - это число типа ULONG_PTR, что означает просто «целое число, равное размеру указателя», поэтому он является 32-битным для x86 и 64-битным для x64. Имя типа сбивает с толку, но когда имена типов win32 ссылаются на указатель, они делают это, ставя P перед именем, а не в конце.

person Stewart    schedule 02.04.2010

Если ваше приложение является многопоточным, убедитесь, что переданный вами CompletionKey является либо константой, либо значением указателя на объект в куче, а не в стеке. В вашем примере, где 100 передается как константа, вы, должно быть, ошиблись, сказав какое-либо изменение. Но что касается проблемы, возможно, вы передаете дескриптор сокета в CreateIoCompletionPort, но не ссылку для обработки в GetQueuedCompletionStatus, чтобы получить его. Ты можешь сделать

HANDLE socket;
CreateIoCompletionPort((HANDLE)socket, existed_io_completion_port, (ULONG_PTR)socket, 0);
/*some I/Os*/
...

и

HANDLE socket;
GetQueuedCompletionStatus(existed_io_completion_port, &io_bytes_done, (PULONG_PTR)&socket, &overlapped);

и обратите внимание на тип в скобках.

person Y.Z    schedule 19.10.2011