Поделиться через


Жизненный цикл буфера памяти

Жизненный цикл буфера памяти охватывает время от момента создания буфера до момента его удаления. В этом разделе описываются сценарии использования буфера и их влияние на удаление буфера.

На платформе драйвера режима ядра (KMDF) объект запроса представляет запрос ввода-вывода. Каждый объект запроса связан с одним или несколькими объектами памяти, и каждый объект памяти представляет буфер, который используется для ввода или вывода в запросе.

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

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

Драйвер на основе платформы также может создавать собственные объекты запросов для отправки в целевые объекты ввода-вывода. Созданный драйвером запрос может повторно использовать существующий объект памяти, полученный драйвером в запросе ввода-вывода. Драйвер, который часто отправляет запросы к целевым объектам ввода-вывода, может повторно использовать создаваемые им объекты запросов .

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

Рассмотрим следующие сценарии использования.

Сценарий 1. Драйвер получает запрос ввода-вывода от KMDF, обрабатывает его и завершает.

В простейшем сценарии KMDF отправляет запрос драйверу, который выполняет операции ввода-вывода и завершает запрос. В этом случае базовый буфер мог быть создан приложением в пользовательском режиме, другим драйвером или самой операционной системой. Сведения о доступе к буферам см. в разделе Доступ к буферам данных в драйверах Framework-Based.

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

Сценарий 2. Драйвер получает запрос ввода-вывода от KMDF и перенаправит его в целевой объект ввода-вывода.

В этом сценарии драйвер перенаправит запрос в целевой объект ввода-вывода. В следующем примере кода показано, как драйвер извлекает дескриптор объекта памяти из объекта входящего запроса, форматирует запрос для отправки в целевой объект ввода-вывода и отправляет запрос:

VOID
EvtIoRead(
    IN WDFQUEUE Queue,
    IN WDFREQUEST Request,
    IN size_t Length
    )
{
    NTSTATUS status;
    WDFMEMORY memory;
    WDFIOTARGET ioTarget;
    BOOLEAN ret;
    ioTarget = WdfDeviceGetIoTarget(WdfIoQueueGetDevice(Queue));

    status = WdfRequestRetrieveOutputMemory(Request, &memory);
    if (!NT_SUCCESS(status)) {
        goto End;
    }

    status = WdfIoTargetFormatRequestForRead(ioTarget,
                                    Request,
                                    memory,
                                    NULL,
                                    NULL);
    if (!NT_SUCCESS(status)) {
        goto End;
    }

    WdfRequestSetCompletionRoutine(Request,
                                    RequestCompletionRoutine,
                                    WDF_NO_CONTEXT);

    ret = WdfRequestSend (Request, ioTarget, WDF_NO_SEND_OPTIONS);
    if (!ret) {
        status = WdfRequestGetStatus (Request);
        goto End;
    }

    return;

End:
    WdfRequestComplete(Request, status);
    return;

}

Когда целевой объект ввода-вывода завершил запрос, платформа вызывает обратный вызов завершения, заданный драйвером для запроса. В следующем коде показан простой обратный вызов завершения:

VOID
RequestCompletionRoutine(
    IN WDFREQUEST                  Request,
    IN WDFIOTARGET                 Target,
    PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,
    IN WDFCONTEXT                  Context
    )
{
    UNREFERENCED_PARAMETER(Target);
    UNREFERENCED_PARAMETER(Context);

    WdfRequestComplete(Request, CompletionParams->IoStatus.Status);

    return;

}

Когда драйвер вызывает WdfRequestComplete из обратного вызова завершения, платформа удаляет объект памяти. Теперь дескриптор объекта памяти, полученный драйвером, недопустим.

Сценарий 3. Драйвер выдает запрос ввода-вывода, использующий существующий объект памяти.

Некоторые драйверы выдают собственные запросы ввода-вывода и отправляют их целевым объектам ввода-вывода, которые представлены целевыми объектами ввода-вывода. Драйвер может создать собственный объект запроса или повторно использовать созданный платформой объект запроса. Используя любой из этих способов, драйвер может повторно использовать объект памяти из предыдущего запроса. Драйвер не должен изменять базовый буфер, но он может передать смещение буфера при форматировании нового запроса ввода-вывода.

Сведения о форматировании нового запроса ввода-вывода, использующего существующий объект памяти, см. в разделе Отправка запросов ввода-вывода в общие целевые объекты ввода-вывода.

Когда платформа форматирует запрос для отправки в целевой объект ввода-вывода, она извлекает ссылку на объект переработанной памяти от имени целевого объекта ввода-вывода. Целевой объект ввода-вывода сохраняет эту ссылку до выполнения одного из следующих действий:

  • Запрос завершен.
  • Драйвер повторно переформатирует объект запроса, вызывая один из методов WdfIoTargetFormatRequestXxx или WdfIoTargetSendXxxSynchronously . Дополнительные сведения об этих методах см. в разделе Framework I/O Target Object Methods.
  • Драйвер вызывает WdfRequestReuse.

После завершения нового запроса ввода-вывода платформа вызывает обратный вызов завершения ввода-вывода, заданный драйвером для этого запроса. На этом этапе целевой объект ввода-вывода по-прежнему содержит ссылку на объект памяти. Таким образом, в обратном вызове завершения ввода-вывода драйвер должен вызвать WdfRequestReuse для созданного драйвером объекта запроса, прежде чем завершить исходный запрос, из которого он извлек объект памяти. Если драйвер не вызывает WdfRequestReuse, проверка возникает ошибка из-за дополнительной ссылки.

Сценарий 4. Драйвер выдает запрос ввода-вывода, использующий новый объект памяти.

Платформа предоставляет три способа создания новых объектов памяти драйверами в зависимости от источника базового буфера. Дополнительные сведения см. в разделе Использование буферов памяти.

Если буфер выделен платформой или из созданного драйвером списка lookaside, буфером владеет объект памяти, поэтому указатель буфера остается действительным до тех пор, пока существует объект памяти. Драйверы, которые выдают асинхронные запросы ввода-вывода, всегда должны использовать буферы, принадлежащие объектам памяти, чтобы платформа удостоверялась, что буферы сохранялись до тех пор, пока запрос ввода-вывода не будет выполнен обратно в выдающий драйвер.

Если драйвер назначает ранее выделенный буфер новому объекту памяти путем вызова WdfMemoryCreatePreallocated, объект памяти не является владельцем буфера. В этом случае время существования объекта памяти и время существования базового буфера не связаны. Драйвер должен управлять временем существования буфера и не должен пытаться использовать недопустимый указатель буфера.

Сценарий 5. Драйвер повторно использует созданный объект запроса.

Драйвер может повторно использовать создаваемые объекты запроса, но он должен повторно инициализировать каждый такой объект, вызывая WdfRequestReuse перед каждым повторным использованием. Дополнительные сведения см. в разделе Повторное использованием объектов запроса платформы.

Пример кода, который повторно инициализирует объект запроса, см . в примерах NdisEdge и NdisEdge , которые предоставляются вместе с выпуском KMDF.