Программирование драйверов Windows

Context


Контекст. Этот термин употребляется программистами для обозначения двух существенно различающихся явлений.

  • Когда производится регистрация callback функции, производится определения параметра, который она получит в качестве аргумента при вызове. Как правило, это указатель на буфер с данными, которые callback функции необходимо знать, чтобы ориентироваться, зачем же ее вызвали. В драйверах чаще всего в роли контекста (контекстного указателя) выступает указатель на структуру описания устройства или структуру описания расширения устройства, например при регистрации DpcForISR (см. ниже). Именно так чаще всего и следует трактовать словосочетание "in device context" - в контексте устройства, иными словами: применительно к данному устройству.

  • Структура, отражающая состояние программного потока на данный момент. Создаваемая операционной системой Windows для каждого потока, служит, например, для запоминания состояния регистров и некоторой другой информации на момент окончания последнего по времени кванта времени, выделенного потоку. Структура контекста потока режима ядра несколько проще, чем контекста потока пользовательского режима. Для драйвера к контексту (по сути этого понятия) следует отнести также состояние объектов, которыми он владеет, IRP пакеты (см. ниже) в очереди, к которым он может получить доступ, и даже состояние обслуживаемого устройства.
  • Вольное, но все еще допустимое, значение слова "контекст" подразумевает некие признаки потока, вызвавшего одну из процедур драйвера (Driver Routine), которые перешли на вызванный код. С этой точки зрения, код драйвера режима ядра может выполняться в одном из трех контекстов:

  • в контексте свойств пользовательского потока, который инициировал обращение к драйверу
  • в контексте системного рабочего потока режима ядра
  • как результат поступления сигнала прерывания от аппаратуры, то есть ни в каком из контекстов существующих потоков или процессов, которые были текущими в момент прерывания
  • Приняв это определение, несложно понять, почему становится возможным благополучное разрешение следующей ситуации.
    Предположим, в некотором драйвере рабочая (dispatch) процедура, предназначенная для обработки IOCTL запросов от пользовательских приложений, получает при методе буферизации METHOD_NEITHER виртуальный адрес буфера с пользовательскими данными (или для пользовательских данных). Этот адрес поступит в драйвер в том самом виде, как он был виден в пользовательском приложении (с адресом меньше 0x80000000, например, 0x00012A0). Однако этот виртуальный пользовательский адрес имеет смысл только в адресном пространстве вызывающего пользовательского приложения (такое же число в качестве адреса в другом приложении пользовательского режима указывало бы на совершенно другую область памяти) - и это одно из неотъемлемых свойств виртуальной адресации. Должен произойти крах системы?

    Тем не менее, драйвер, к которому могут обратиться с подобными запросами разные пользовательские приложения (причем, почти одновременно), выполнит свою работу абсолютно корректно - данные попадут по назначению. Почему? Потому что рабочая процедура, обрабатывающая IOCTL запросы от клиентов драйвера, работает в контексте вызвавших ее потоков, вследствие чего трактовка виртуальных адресов в подобных случаях не вызывает у операционной системы никаких затруднений. Драйвер получает доступ к нужной области памяти, и при этом никакой ошибки доступа к, на первый взгляд, "чужой" памяти не возникает.


    Содержание раздела