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


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

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

В платформе драйверов в режиме ядра (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. Драйвер выдает запрос ввода-вывода, использующий существующий объект памяти.

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

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

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

  • Запрос завершен.
  • Драйвер повторно переформатирует объект запроса, вызвав один из методов WdfIoTargetFormatRequestXxxx илиWdfIoTargetSendXxxxsynchronous. Для получения дополнительной информации об этих методах см. раздел «Методы целевого объекта I/O в Framework».
  • Драйвер вызывает WdfRequestReuse.

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

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

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

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

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

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

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

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