Особенности реализации NDIS-драйвeров
Особенности реализации NDIS-драйвeров
Среда NDIS и NDIS-драйверы ранее уже описывались в разделе «Среда NDIS и NDIS драйверы». Хотя по требованиям переносимости NDIS-драйвер должен пользоваться только функциями, экспортируемыми средой NDIS, в реальных приложениях ничто не запрещает использовать функции ядра той ОС, под которую реализуется этот драйвер.
Переносимость и поддержка многопроцессорности. Драйвер должен быть написан на C/C++, причем необходимый формат результирующего файла может быть сгенерирован только компиляторами фирмы Microsoft. Драйвер должен избегать любой зависимости от типов данных, чей размер различается между платформами. Драйвер не должен вызывать никаких функций стандартных библиотек С, кроме функций обеспечиваемых NDIS. В режиме ядра не разрешены никакие операции с плавающей точкой.
В многопроцессорной среде процессоры одновременно выполняют несколько машинных инструкций. И поэтому драйвер должен синхронизировать доступ к общим структурам данных, чтобы в то время, когда одна функция драйвера обрабатывала общие данные, другая функция (или та же самая), исполняемая на другом процессоре, не попыталась изменить общие данные в тот же момент времени. Чтобы исключить эту проблему драйверы используют спин-блокировки.
Уровни IRQL исполнения функций драйвера. Любая функция драйвера, вызываемая NDIS, исполняется с уровнем приоритета, определяемым операционной системой, и варьирующимся между PASSIVEJLEVEL < DISPATCH_ LEVEL < DIRQL. Например, функция инициализации минипорта, функции останова, сброса и закрытия обычно исполняются с приоритетом PASSIVE_ LEVEL. Все другие функции NDIS-драйвера исполняются на уровне IRQL меньшем или равном DISPATCHJLEVEL. Промежуточный NDIS-драйвер или драйвер протокола никогда не исполняются на уровне DIRQL.
Уровень приоритета потока драйвера влияет на то, какие функции NDIS библиотеки он может вызывать. Некоторые функции могут вызываться только, если уровень приоритета PASSIVE_LEVEL, другие - если DISPATCH_LEVEL или ниже.
Выгружаемый и уничтожаемый код драйвера. Функции драйвера, которые всегда выполняются с уровнем приоритета PASSIVE_LEVEL, могут быть помечены как pageable (выгружаемые), используя макрос NDIS_PAGEABLE_ FUNCTION. При разработке драйверов поощряется помечать код как выгружаемый где это возможно, освобождая тем самым системную область памяти для кода, который должен быть резидентным. Функция драйвера, которая исполняется с приоритетом PASSIVEJLEVEL, может быть помечена как выгружаемая, если она сама не вызывает или ее не вызывает любая функция, которая исполняется с приоритетом большим или равным DISPATCH_LEVEL. (Например, если она не вызывает функцию, которая захватывает спин-блокировку, так как ее захват вызывает повышение уровня приоритета захватывающего потока до уровня DISPATCH_LEVEL).
Функция DriverEntry промежуточного драйвера должна быть определена как функция инициализации, используя макрос NDIS_INIT_FUNCTION. Предполагается, что код, помеченный этим макросом, исполняется только один раз во время инициализации системы, в результате чего он присутствует в оперативной памяти только в это время. После того как функция DriverEntry вернет управление, код, помеченный как NDIS_INIT_FUNCTION, уничтожается из оперативной памяти.
Сериализация вызовов функций. Среда NDIS обеспечивает сериализацию вызовов точек входа NDIS-драйверов, но при этом существует исключение. Начиная с некоторых версий ОС, появилось понятие десериализованного минипорта, где по требованиям производительности полнодуплексного обмена данными, реализация сери-ализации передачи пакетов протоколам возложена на разработчика минипорта.