Еще раз о Windows и реальном времени

Первая публикация 17.03.2014

About Windows and real time again

Введение

Одна из типичных ситуаций: ноутбук с 64-разрядной Windows 7, на котором работает прикладная программа, обрабатывающая данные, регулярно приходящие из сети или от некоторой аппаратуры. Все функционирует, как задумано, кроме того, что иногда возникают непредсказуемые задержки, связанные с работой самой Windows, т.е. планировщика. Это ожидаемо, так как и Windows не система реального времени (больше подходит название «система мягкого реального времени»), да и ноутбук вовсе не специальный сервер, предназначенный для транзакций в реальном времени.

Обидно, что для преодоления этого последнего и, казалось бы, несущественного препятствия приходится прикладывать значительные усилия. Предлагается и переход на другие операционные системы и надстройки над Windows (по существу ее подмена) вроде среды RTX, и написание драйверов режима ядра, что выглядит иногда как стрельба из пушек по воробьям. Однако иначе невозможно преодолеть принципиальное ограничение Windows, запрещающее прикладным программам монопольно владеть ресурсами в обход использования механизма драйверов, написание которых становится все сложнее и сложнее и уже требует цифровой подписи и других особенностей, никак не связанных с решаемыми задачами.

На мой взгляд, данное несоответствие обусловлено противоречием между общим назначением Windows и конкретностью частных задач. В общем случае разработчики ОС справедливо не допускают прикладные задачи до полного контроля над компьютером (помимо использования аппарата драйверов), иначе устойчивая работа системы станет невозможной. Но в данном конкретном случае компьютер и предустановленная на нем Windows куплены только ради одной прикладной программы и как раз в этом случае данная программа вполне могла бы монопольно владеть ресурсами, причем других прикладных задач при ее работе нет.

Аппаратная поддержка

Разработчики архитектуры х86 предусмотрели некоторую поддержку подобных ситуаций [1]. Речь идет об уровне привилегий ввода-вывода IOPL (биты 12 и 13) регистра флагов EFLAGS. Если установить оба этих бита в единицу (т.е. сделать IOPL=3), то обычная прикладная программа сможет напрямую обращаться к портам ввода-вывода и, главное, сможет прямо задавать команды CLI/STI, закрывающие и открывающие прерывания.

Имея нужный уровень IOPL, прикладная программа могла бы выполнить очередную транзакцию гарантированно без переключений в это время на другие задачи. В предельном случае прикладная программа вообще могла бы провести целый сеанс, выключив, точнее «заморозив» Windows, а затем включив ее снова. Это вполне возможно, хотя и потребует после такой «разморозки» ОС некоторых дополнительных действий, вроде коррекции программных часов Windows по аппаратным часам компьютера. В любом случае приостанавливается работа планировщика, и влияние Windows в этот момент полностью исключается.

Таким образом, не используя надстройки или другие ОС и не разрабатывая драйверы, можно было бы вернуться в некотором роде к временам Windows-98 или даже MS-DOS, когда прикладная программа могла в принципе делать что угодно (и поэтому зависание и крах системы были обычным делом), однако сложностей с реальным временем, по сути, не было.

Исправление ядра

Документированного, а возможно и недокументированного способа установить IOPL=3 в Windows 7, скорее всего, нет и не предвидится, несмотря на глухие упоминания о функции с многообещающим названием ke386SetIopl. Как же быть? А можно ли тогда «исправить» саму Windows 7? Ну, хотя бы всего один конкретный экземпляр на одном конкретном компьютере? Попробуем.

Прежде всего, исправлять придется ядро Windows 7 в его 64-разрядном варианте, т.е. файл ntoskrnl.exe из папки Windows\system32, причем после загрузки под этим именем там находится совсем другой файл – 32-х разрядная версия ядра, которая вообще не используется на 64-х разрядных процессорах. Можете сами в этом убедиться, сделав себя владельцем этого файла и переименовав или даже стерев его. 64-разрядная Windows 7 после перезагрузки будет работать, как ни в чем не бывало и 32-х разрядные приложения нормально выполняются. То же справедливо и для файла ntkrnlpa.exe (32-х разрядной версии ядра для памяти более 3 Гбайт): т.е. и он присутствует, но никак не используется.

Для доступа к файлу настоящего ядра нужно, например, загрузить другую ОС с компакт-диска, используя удобные специальные сборки типа «Реаниматор», обеспечивающие доступ к исходным дискам C:, D: и т.д.

Кроме этого, в Windows 7 имеется документированный способ подключить другое ядро, например, файл с именем nt1skrnl.exe с помощью директивы:

BCDEDIT /set {current} kernel “nt1skrnl.exe”

Откуда возьмется другое ядро? Для начала можно просто скопировать в системной папке под таким именем исходное. Теперь все опыты можно вести с копией в nt1skrnl.exe, не трогая исходного ядра. И всегда можно вернуться к исходному ядру опять скопировав его под этим именем или выполнив директиву подключения стандартного ядра:

BCDEDIT /set {current} kernel “ntoskrnl.exe”

Файл ntoskrnl.exe представляет собой несколько мегабайт команд в 64-х разрядном режиме. Куда же в нем вставлять команды установки IOPL?

Подсказку дает необходимость в Windows следить за содержимым регистра флагов у прикладных задач. Ядро постоянно «приводит в порядок» флаги задачи пользовательского уровня, гася запрещенные по маске-константе. Младшая часть этой константы 0DD5 и «выдает» работу с флагами пользовательской программы, например:

002AF8E1 8B87C0000000…….MOV……EAX,[EDI]+000000C0
002AF8E7 25D50D3F00………..AND…….EAX,003F0DD5
002AF8EC 0D02020000……….OR……….EAX,00000202
002AF8F1 8986C0000000…….MOV…….[ESI]+000000C0,EAX

Здесь ядро погасило запрещенные биты, в том числе и IOPL, а затем принудительно разрешило флаг прерываний. Достаточно заменить одну команду на другую:

002AF8EC 0D02320000      OR     EAX,00003202

и при выходе из ядра в пользовательский уровень IOPL станет равным 3.

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

002E24ED 25D50D2100…..AND…..EAX,00210DD5
002E24F2 0FBAE809………BTS…….EAX,09
002E24F6 898178010000..MOV……[ECX]+00000178,EAX

Правда, здесь флаг разрешения прерывания устанавливается почему-то экзотическим способом с помощью команды BTS, очевидно как более короткой. Однако, прямая установка командой:

002E24F2 660D0002        OR     AX,0200

ничуть не длиннее. Используя эту команду, устанавливаем заодно и максимальный уровень IOPL:

002E24ED 25D50D2100……..AND……EAX,00210DD5
002E24F2 660D0032………….OR………AX,3200
002E24F6 898178010000……MOV……[ECX]+00000178,EAX

Поскольку изменились байты, контрольная сумма по адресу 00000140 (которую можно рассчитать с помощью CheckSumMappedFile) также должна поменяться, в данном случае с 5553E5 на 55С260

Таким образом, после всех исправлений, список отличий от исходного ядра, выведенный с помощью стандартной утилиты fc, следующий:

fc /b nt1skrnl.exe ntoskrnl.exe

Сравнение файлов nt1skrnl.exe и ntoskrnl.exe

00000140: 60 E5
00000141: C2 53
002E24F1: 66 0F
002E24F2: 0D BA
002E24F3: 00 E8
002E24F4: 32 09

Тестирование исправления

Теперь можно производить перезагрузку Windows 7 с входом в расширенное меню по нажатию F8 и выбора режима «отключение обязательной проверки подписи драйверов», поскольку цифровая подпись уже не совпадает. В результате исправленное ядро из файла nt1skrnl.exe будет загружено и запущено.

Проверим работу исправления ядра на примере простейшей 32-х разрядной тестовой программы на языке PL/1 [2]:

test:proc main;
dcl f float(53);
put skip list(‘start’);
unspec(‘fa’b4);……………….// закрыли прерывания командой CLI
do f=0e0 to 1e9; f=f; end; // имитация длительной работы
unspec(‘fb’b4);………………// опять открыли прерывания командой STI
put skip list(‘finish’);
end test;

При запуске выдается сообщение «start» затем на десяток секунд вся система «замирает» (например, мышь не перемещается), затем выдается «finish» и нормальная работа Windows 7 возобновляется.

Заключение

Итак, на том же самом компьютере, на той же самой ОС и почти не меняя прикладной программы (были только добавлены команды CLI/STI) удалось решить поставленную задачу за счет нетривиальной, но примитивной «доработки» самой ОС. При этом был получен режим «реального времени» в том смысле, что исключено вмешательство планировщика и служебных процессов во время работы прикладной программы. Конечно, подобного же эффекта можно достичь и через аппарат драйверов, но это требует значительно больших изменений в построении ПО, а кроме этого не используется аппаратная поддержка, предусмотренная разработчиками архитектуры х86 для прикладных программ в подобных случаях. Причем такая поддержка позволяет не переходить с пользовательского режима на уровень ядра.

Простым исправлением удалось преодолеть противоречие между общим назначением Windows и требованием конкретной задачи. В результате исправления частично произошел возврат к идеологии ОС эпохи Windows 98, когда прикладная задача могла захватить компьютер в монопольное использование и максимально быстро реагировать на происходящие события. Хотя такой режим в общем случае недопустим, для частных задач его вполне можно использовать и было бы целесообразно иметь подобный режим как документированное средство Windows. Это позволило бы проще применять данную ОС для более широкого класса задач.

Литература

  1. Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 1. Order Number:253665-029US, November 2008
  2. Караваев Д.Ю. К вопросу о совершенствовании языка программирования RSDN Magazine #4, 2011
0

Автор публикации

не в сети 3 месяца

admin

3
Комментарии: 28Публикации: 173Регистрация: 13-06-2019
Авторизация
*
*

десять + 5 =

Регистрация
*
*
*

три × 2 =

Генерация пароля

11 − 5 =

Перевести »
Прокрутить вверх
Scroll to Top