Статья посвящена практическим аспектам написания драйверов уровня ядра в операционных системах семейства NT — Windows 2000/XP (XP Embedded). Для понимания работы подсистемы ввода-вывода, составляющим звеном которой являются драйверы, дано краткое описание структурной организации ОС в целом. Приводится рабочий пример простейшего драйвера уровня ядра.
Современный уровень аппаратных средств, используемых в автоматизации, давно «размыл» чёткую границу между компьютерами верхнего уровня и контроллерами полевого уровня, с точки зрения используемого системного программного обеспечения. Аппаратные ресурсы современных контроллеров позволяют использовать на них не только оптимизированные к минимальным аппаратным требованиям операционные системы, например Windows CE.NET, но и стандартные ОС класса Windows или Linux. Кроме того, из встраиваемых, но более ресурсоёмких операционных систем, всё более активно применяющихся на рынке автоматизации отечественной промышленности, можно назвать Embedded Windows XP, общая структурная организация которой во многом схожа с организацией стандартного (офисного) варианта. Возможность «конструирования» разработчиком образа этой ОС из большого числа компонентов не влияет на основные принципы общей организации ОС в части затрагиваемых в этой статье вопросов, поэтому рассматриваемые здесь возможности написания драйверов уровня ядра применимы и для этой ОС. На текущий момент доминирующее положение среди стандартных ОС Windows в сфере автоматизации занимают ОС Windows 2000/XP, поэтому дальнейшее изложение материала касается именно этих ОС. Использование стандартных ОС позволяет максимально использовать опыт программирования, накопленный специалистами, привыкшими работать со знакомым набором API (Application Programming Interface — интерфейс прикладного программирования). В числе решаемых программистами задач, кроме собственно написания прикладной программы, может возникнуть необходимость в написании драйвера, например, если по
Для решения поставленной задачи используем пакет MASM32
В Windows 2000/XP существует чёткое разграничение двух областей в оперативной памяти и режимов процессора для исполняемого кода: 1) область исполняемого кода в непривилегированном режиме работы процессора (пользовательском режиме) для приложений пользователя и части компонентов ОС, и область исполняемого кода операционной системы в привилегированном режиме процессора (режиме ядра). Под областью исполняемого кода надо понимать области загрузки (диапазон адресов) в оперативной памяти вычислительной системы. Windows 2000/XP —
Как уже отмечалось, в режиме пользователя функционируют не только прикладные программы пользователя, но и часть процессов самой ОС.
К компонентам операционной системы, работающим в режиме пользователя, относятся:
К компонентам ОС, работающим в режиме ядра, относятся:
Драйверы устройств в Windows, в отличие от DOS, для поддержки переносимости не обращаются к оборудованию напрямую, а используют функции, предоставляемые HAL. Драйверы устройств режима ядра делятся на следующие основные категории:
В свою очередь, в каждой из категорий есть группы драйверов, которые различаются в зависимости от модели устройства и места драйверов в цепочке обработки запроса на обслуживание операций
Начиная с Windows 2000, была введена поддержка PnP и энергосберегающих технологий (ACPI), что привело к расширению модели драйверов, называемой Windows Driver Model (WDM); напомню, что речь идёт о линейке NT, модель драйверов WDM ранее реализована в Windows 98 и Windows Millennium Edition. ОС Windows 2000 и более поздние версии ОС линейки NT поддерживают и так называемые унаследованные драйверы (NT4), естественно, с некоторой потерей функциональности.
Модель WDM предусматривает существование трёх типов драйверов:
В рамках обобщения понятия устройства в Windows существует понятие класса устройств. Введение этого уровня абстрагирования сопровождается неизбежным появлением типа драйверов, отвечающих за обслуживание устройств одного класса (например
Драйверы устройств в ОС Windows могут работать как в режиме ядра, так и в пользовательском режиме. К последним относятся:
Важнейшим компонентом исполнительной системы, отвечающим за связь с устройствами, является подсистема
Подсистема
Сервисы (Services), или службы, являются процессами, предоставляющими дополнительную функциональность в системе, не зависящую от интерактивных действий пользователя. То есть это приложения, запускаемые без учёта того, зарегистрировался ли в системе
Весь необходимый API для реализации механизма диспетчеризации (
Сервисное приложение — это драйвер или обычное
SCP — стандартное
SCM отвечает за загрузку как сервисов, так и драйверов, поэтому не удивительно, что значения некоторых параметров явным образом указывают на контекст текущего приложения. Общий термин (Services), используемый Microsoft для обозначения как служб, так и драйверов устройств (они действительно во многом схожи по стилю работы и характеру выполняемых функций), часто вызывает путаницу в понимании программной документации. Наиболее важные для нашей прикладной задачи параметры созданного раздела реестра и их возможные значения для драйверов и сервисов показаны в таблице 1.
На конечном этапе загрузки ОС системный процесс Winlogon (перед появлением диалогового окна с приглашением к регистрации) запускает SCM, который в созданной внутренней базе данных сервисов SCM ищет записи драйверов и сервисов с параметром Start, имеющим значение SERVICE_AUTO_START, и запускает их.
Далее рассмотрим пример реализации SCP на языке ассемблера (листинг 1). Минимально необходимый материал, позволяющий понять правила и нотацию для оформления программ на этом языке, можно найти в [3].
Действия программы заключаются в следующем. Устанавливается канал связи с SCM. Если при установлении канала связи происходит ошибка, выводится сообщение «Attempt of connection with SCM has failed!» и программа завершается. В случае установления канала регистрируем драйвер. Если происходит ошибка регистрации, выводим соответствующее сообщение «Attempt to register the driver has failed!» и, закрыв дескриптор SCM, завершаем программу. В случае успеха регистрации драйвера запускаем драйвер, удаляем его, закрываем дескриптор драйвера, закрываем дескриптор SCM, завершаем программу. В этой программе драйвер запускается один раз, и поэтому его действия носят характер, только подтверждающий факт его работы. Теперь более подробно.
Для вызова
OpenSCManager proto
lpMachineName:LPSTR,
lpDatabaseName:LPSTR,
wDesiredAccess:DWORD
lpMachineName
Указатель на строку, завершающуюся нулём, содержащую имя компьютера. Устанавливая этот параметр в NULL, мы устанавливаем связь с локальным SCM (на этом компьютере).
lpDatabaseName
Указатель на строку, завершающуюся нулём, содержащую имя открываемой базы. Устанавливая этот параметр в NULL, мы устанавливаем связь с локальной, активной в текущий момент базой данных — SERVICES_ACTIVE_DATABASE.
dwDesiredAccess
Права доступа, запрашиваемые при открытии канала. Могут быть следующие значения:
SC_MANAGER_CONNECT (устанавливаются по умолчанию, параметр 0);
SC_MANAGER_CREATE_SERVICE (доступ для внесения в базу данных записи о новом драйвере);
SC_MANAGER_ALL_ACCESS (полный доступ).
Если эта функция возвращает не NULL (NULL говорит об ошибке), то мы получаем дескриптор активной базы данных. Следующий шаг — это регистрация нашего драйвера путём вызова функции CreateService, прототип функции выглядит так:
CreateService proto
hSCManager:HANDLE,
lpServiceName:LPSTR,
lpDisplayName:LPSTR,
dwDesiredAcces:DWORD,
dwServiceType:DWORD,
dwStartType:DWORD,
dwErrorControl:DWORD,
lpBinaryPathName:LPSTR,
lpLoadOrderGroup:LPSTR,
lpdwTagId:LPDWORD,
lpDependencies:LPSTR,
lpServiceStartName:LPSTR,
lpPassword:LPSTR
hSCManager
Дескриптор базы данных SCM.
lpServiceName
Указатель на строку, завершающуюся нулём, содержащую имя драйвера/сервиса. Длина до 256 символов. Соответствует имени подраздела в реестре.
lpDisplayName
Указатель на строку, завершающуюся нулём, содержащую экранное имя драйвера/сервиса. Длина до 256 символов. Соответствует значению параметра DisplayName в реестре.
dwDesiredAcces
Запрашиваемый тип доступа. Могут быть значения: SERVICE_ALL_ACCESS (полный доступ); SERVICE_START (доступ на запуск драйвера/сервиса); SERVICE_STOP (доступ на останов драйвера/сервиса); DELETE (доступ на удаление драйвера/сервиса из базы SCM).
dwServiceType
Тип сервиса, в нашем случае SERVICE_KERNEL_DRIVER.
Соответствует значению параметра TYPE в реестре.
dwStartType
Тип запуска, в нашем случае SERVICE_DEMAND_START.
Соответствует значению параметра START в реестре.
dwErrorControl
Характер контроля ошибок,
в нашем случае SERVICE_ERROR_IGNORE.
Соответствует значению параметра ErrorControl в реестре.
lpBinaryPathName
Указатель на строку, завершающуюся нулём, содержащую полный путь к файлу загружаемого драйвера. Соответствует значению параметра ImagePath в реестре.
lpLoadOrderGroup
Указатель на строку, завершающуюся нулём, содержащую имя группы, в случае если загружаемый драйвер является членом группы. В противном случае значение NULL или указатель на пустую строку.
lpdwTagId
Указатель на переменную, содержащую уникальное значение тега, идентифицирующее группу. В противном случае NULL.
lpDependencies
Указатель на массив имен драйверов или групп драйверов, загрузка которых должна быть осуществлена до запуска текущего драйвера. Массив заканчивается двумя нулями, имена драйверов или групп разделяются одним нулём. Если запуск драйвера не связан с предварительным запуском других драйверов, значение NULL.
lpServiceStartName
Указатель на строку, завершающуюся нулём, содержащую имя учётной записи (account), с правами которой запускается текущий драйвер. В случае типа сервиса SERVICE_KERNEL_DRIVER параметр содержит имя объекта драйвера. Если используется имя объекта драйвера, присвоенное подсистемой
lpPassword
Указатель на строку, завершающуюся нулём, содержащую пароль учётной записи, с правами которой запускается текущий драйвер. В случае SERVICE_KERNEL_DRIVER значение этого параметра игнорируется.
Остальные функции — StartService, DeleteService и CloseServiceHandle — запускают, удаляют и закрывают дескриптор нашего драйвера. Далее приводятся прототипы функций и описания их параметров.
StartService proto
hService:HANDLE,
dwNumServiceArgs:DWORD,
lpServiceArgVectors:LPSTR
hService
Дескриптор драйвера/сервиса.
dwNumServiceArgs
Количество аргументов, передаваемых сервису. Драйверу не передаются аргументы, поэтому значения NULL.
lpServiceArgVectors
Указатель на массив указателей, ссылающихся на строки, завершающиеся нулём. В строках содержатся передаваемые службе аргументы. В нашем случае аргументы отсутствуют, поэтому значение NULL.
DeleteService proto hService: HANDLE
hService
Дескриптор удаляемой службы.
CloseServiceHandle proto hSCObject:HANDLE
hSCObject
Дескриптор закрываемого SCM, сервиса, драйвера.
Рассмотрим шаблон простейшего драйвера (листинг 2).
Не правда ли, если Вы знакомы с динамически подключаемой библиотекой (DLL) [3], то приведённый пример во многом напомнит Вам структуру простейшей динамической библиотеки. Надо сказать, что между драйверами и DLL очень много общего. После загрузки драйвера операционная система передаёт управление на его точку входа. Предполагается, что выполняемая функция программы, которой передано управление сразу после загрузки драйвера, — это инициализация структур и переменных, необходимых для дальнейшей работы драйвера, и отчёт перед ОС о выполненной задаче («DriverEntry is the first routine called after a driver is loaded, and is responsible for initializing the driver», — читаем мы в MSDN). Точкой входа является метка, указанная после директивы End, то есть в нашем случае это EntryDriverUNIO (у Вашего драйвера может быть другое имя). В нашем примере после передачи управления на точку входа происходит формирование перехода из 0 в 1 на одном из выходов
Рассмотрим прототип DriverEntry (у нас это процедура EntryDriverUNIO).
DriverEntry proto
DriverObject:PDRIVER_OBJECT,
RegistryPath:PUNICODE_STRING
DriverObject
Указатель на объект созданного драйвера. Если говорить проще, то речь идёт о структуре типа DRIVER_OBJECT, описанной в файле NTDDK.h DDK. Часть полей в этой структуре необходимо заполнить загруженному драйверу, именно по этой причине ему и передаётся указатель на эту структуру. В нашем примере мы не занимались этой работой, так как «не задерживаемся надолго». В полнофункциональном драйвере эту работу придётся проделать.
RegistryPath
Указатель на структуру типа UNICODE_STRING, содержащую указатель на
Надо особо подчеркнуть, что, в отличие от пользовательского режима, в режиме ядра ОС работает только со строками типа UNICODE_STRING. Теперь несколько слов о компиляции и компоновке драйвера. Строка компиляции достаточно традиционна и, если мы работаем с masm32, выглядит следующим образом:
ML /nologo /c /coff CardUNIO.asm
Опции компоновщика, естественно, отличаются от опций для стандартного исполняемого файла:
LINK /nologo /driver /base:0×1000 /out:CardUNIO.sys /subsistem:native CardUNIO.obj
/driver
Выходной файл — драйвер.
/base:0×1000
Предопределённый адрес загрузки драйвера.
/out:CardUNIO.sys
Выходной файл драйвера должен иметь соответствующее расширение (по умолчанию — .exe).
/subsistem:native
Тип подсистемы, необходимый для работы выходного файла. Этого вопроса мы кратко касались в данной статье, в части, посвящённой обзору архитектуры Windows. Напомню, что речь шла о существовании трёх подсистем окружения: Win32, POSIX и OS/2. Параметр Native говорит о том, что нет необходимости ни в одной из этих подсистем. Драйвер работает в «родной» или естественной среде, то есть использует базовый API самой ОС Windows.
К сожалению, в статье
В статье изложен минимально необходимый материал, для того чтобы обозначить основные направления развития темы написания драйверов в ОС Windows. Следующим шагом в создании полнофункционального драйвера должно быть создание объекта «устройство» функцией IoCreateDevice в фазе инициализации драйвера (в рамках процедуры EntryDriverUNIO) и достаточно полное изучение структуры DRIVER_OBJECT. Многие поля структуры — это просто указатели на процедуры обработки различных пакетов запросов на
В. Пирогов. Ассемблер для Windows. —
М. Руссинович, Д. Соломон. Внутреннее устройство Microsoft Windows: Windows Server 2003, Windows XP, Windows 2000. —
Валерий Яковлев. Написание пользовательской DLL доступа к универсальному
Автор — сотрудник фирмы ПРОСОФТ
Телефон: (812) 448–04-44
Факс: (812) 448–03-39
Однофазные источники бесперебойного питания Systeme Electric
Почти все современные сферы промышленности, IT-инфраструктура, а также любые ответственные задачи и проекты предъявляют повышенные требования к питающей сети – электропитание должно быть надёжным, стабилизированным и обеспечивать бесперебойную работу. В данной статье мы рассмотрим решения по однофазному бесперебойному питанию от российской компании Systeme Electric. 28.12.2023 СТА №1/2024 883 0 0Однопроводный канал телеметрии по PLC
В статье рассматриваются методы реализации однопроводных каналов передачи данных по силовым электросетям в жилых зданиях, загородных и промышленных помещениях. В качестве информационного провода предлагается использовать проводник «нейтраль» электропроводки. Приводятся анализ возможных конфигураций каналов передачи данных этого типа и результаты экспериментальных проверок. Рассматриваются преимущества новых методов по сравнению с традиционными PLC и области возможного применения данной технологии. 28.12.2023 СТА №1/2024 939 0 0BioSmart Quasar 7 — мал да удал
Компания BIOSMART в пандемийном 2020 году весьма своевременно представила свой первый лицевой терминал Quasar (рис. 1) с диагональю экрана 10 дюймов. Уже в следующем, 2021 году был представлен бесконтактный сканер рисунка вен ладони PALMJET (рис. 2). Ну а в текущем 2023 году компания представила новую уменьшенную модель лицевого терминала Quasar 7 (рис. 3), который смог в компактном корпусе объединить обе передовые технологии бесконтактной биометрической идентификации. 28.12.2023 СТА №1/2024 903 0 0Открытые сетевые платформы — когда сети и вычисления в одном устройстве
Открытая сетевая платформа (ONP) – это мощное средство для реализации как простых, так и масштабных сетей, а также инструмент, который позволяет в одном высокопроизводительном устройстве реализовать целый вычислительный комплекс, объединяющий внутри себя коммутаторы, маршрутизаторы, межсетевые экраны, а также сам сервер обработки данных. Используя все преимущества данной архитектуры, компания AAEON разработала своё решение, сетевую платформу FWS-8600, на базе высокопроизводительных процессоров Intel Xeon Scalable 2-го поколения. В статье раскрыты детали и особенности ONP, характеристики FWS-8600, а также почему использование процессоров Intel Xeon Scalable 2-го поколения значительно увеличивает потенциал платформы. 28.12.2023 СТА №1/2024 861 0 0