для чего нужен планировщик заданий
Планировщик заданий в ОС Windows 10
Содержание
Общая информация
Планировщик заданий — это оснастка mmc (Microsoft Management Console), с помощью которой можно назначить различные задания, которые будут производиться в определенное время или при возникновении определенных событий. Как правило, такие задания применяются для автоматизации отдельных процессов:
Операционная система Windows 10 содержит несколько инструментов для планирования заданий, включая такие, как Планировщик заданий, инструмент командной строки Schtasks и несколько командлетов консоли Windows PowerShell. Эти инструменты можно использовать для планирования заданий как на локальных, так и на удаленных рабочих станциях.
Задания могут иметь разные связанные с ними свойства, включая следующие:
Запуск планировщика заданий
1 способ
Рис.1 Запуск планировщика заданий
По умолчанию консоль подключена к локальному компьютеру. Для работы с заданиями удаленных компьютеров в оснастке Управление компьютером можно щелкнуть ПКМ по корневому узлу Управление компьютером в дереве консоли (левая панель) и в контекстном меню выбрать команду Подключиться к другому компьютеру. В открывшемся диалоговом окне Выбор компьютера установить радиокнопку Другим компьютером и ввести имя требуемого компьютера в соответствующее поле, после чего нажать кнопку OK).
Рис.2 Планировщик заданий
2 способ
3 способ
Рис.3 Запуск планировщика заданий
4 способ
5 способ
Пользовательский интерфейс Планировщика заданий
Панель слева содержит узел Библиотека планировщика заданий, который находится по умолчанию под узлом Планировщик заданий. Узел Библиотека планировщика заданий содержит задачи в виде иерархии узлов. Узел Microsoft, расположенный под узлом Библиотека планировщика заданий, содержит узел Windows, в котором находятся все системные задачи, используемые компонентами операционной системы.
Панель в центре экрана показывает имя и информацию о выбранной задаче. В нижней части центральной панели доступна панель просмотра, в которой показаны подробности по выделенной задаче.
На панели Действия доступны основные действия для выделенной задачи или узла. Новые задачи можно создавать при помощи действия Создать простую задачу, предназначенного для простых задач, или при помощи действия Создать задачу, предназначенного для задач, обладающих расширенным набором функций.
Рис.4 Просмотр и управление запланированными заданиями
Для работы с заданием можно щелкнуть по нему правой кнопкой мыши в основной панели и в контекстном меню выбрать одну из следующих команд:
Чтобы увидеть выполняемые задачи, необходимо щелкнуть ПКМ по узлу Планировщик заданий и в контекстном меню выбрать команду Отображать все выполняемые задачи.
Рис.5 Настройка отображения выполняемых задач
Основные действия в планировщике заданий
Рис.6 Основные действия в Планировщике заданий
Создание планируемых заданий (создание простой задачи)
Рис.7 Создание простой задачи
В данной статье будет приведен пример создания простой задачи, которая бы напоминала пользователю при входе в операционную систему MS Windows 10 о каком-либо событии, например, посещении сайта COMSS.
Рис.8 Создание простой задачи
Рис.9 Создание простой задачи
Рис.10 Создание простой задачи
Рис.11 Создание простой задачи
Рис.12 Создание простой задачи
Рис.13 Результат запланированной задачи
Создание похожей задачи, которая бы была направлена на открытие определенной страницы в каком-либо установленном браузере при входе в операционную систему MS Windows 10
Рис.14 Создание простой задачи
Рис.15 Результат выполненной задачи
Создание планируемых заданий (создание задачи без использования мастера)
Для рассмотрения механизма создание задачи без использования мастера, в статье будет описан пример задачи, с помощью которой ежедневно в 23.00 компьютер в автоматическом режиме завершал бы работу.
Если задание должно выполняться под иной учетной записи, чем учетная запись текущего пользователя, можно нажать кнопку Изменить. В открывшемся диалоговом окне Выбор: «Пользователь» или «Группа» выбрать пользователя или группу, с чьей учетной записью нужно выполнять задание, а затем предоставить необходимые учетные данные.
Рис.16 Создание задачи
Рис.17 Создание задачи
В данном примере, если необходимо ежедневно завершать работу компьютера в 23.00 в окне Создание триггера:
Рис.18 Создание задачи
В данном примере необходимо указать путь к программе shutdown с добавлением параметра /s.
Встроенная утилита shutdown позволяет удаленно или локально выключать, перезагружать систему, а также осуществлять вывод пользователя из текущего сеанса. Параметр /s позволяет осуществить завершение работы компьютера. Утилита shutdown расположена в следующей директории: C:\Windows\System32
Рис.19 Директория, где расположена утилита shutdown
Рис.20 Создание задачи
При наступлении времени завершения работы, указанного в настройках задачи, компьютер будет выключен.
Рис.21 Результат выполнения задачи
Просмотр ранее созданных задач в Планировщике заданий
Чтобы просмотреть ранее созданные задачи необходимо открыть Планировщик заданий и выбрать узел Библиотека планировщика заданий.
Использование Планировщика заданий Windows 10 для автоматизации различных задач
Использование Планировщика заданий позволяет автоматизировать отдельные функции указанных процессов. Данная статья является продолжением статьи Планировщик заданий в ОС Windows 10.
Содержание
Автоматизация задачи создания контрольных точек восстановления системы Windows 10 в Планировщике заданий
При создании контрольных точек восстановления, первое, что необходимо сделать, это активировать механизм восстановления системы. Данный механизм активируется посредством включения функции Защита системы.
Включение системы защиты в ОС MS Windows 10
Рис.1 Настройка защиты системы
Рис.2 Настройка защиты системы
Рис.3 Настройка защиты системы
Рис.4 Настройка защиты системы
Автоматизация задачи создания контрольных точек восстановления системы MS Windows 10 в Планировщике заданий
Рис.5 Редактирование задачи System Restore в Планировщике заданий
Рис.6 Редактирование задачи System Restore в Планировщике заданий
Рис.7 Редактирование задачи System Restore в Планировщике заданий
Рис.8 Редактирование задачи System Restore в Планировщике заданий
В процессе определения периода создания точек, необходимо учитывать объем зарезервированного места на жестком диске. Необходимо помнить, что при превышении объема информации зарезервированного места, новые точки восстановления будут стирать самые ранние.
Рис.9 Редактирование задачи System Restore в Планировщике заданий
Рис.10 Редактирование задачи System Restore в Планировщике заданий
Рис.11 Редактирование задачи System Restore в Планировщике заданий
Рис.12 Результат выполненной задачи в Планировщике заданий
Также результат выполненной задачи можно посмотреть в окне Восстановление системы. Для этого необходимо:
Рис.13 Выбор апплета Восстановление в Панели управления
Рис.14 Запуск восстановления системы
Рис. 15 Мастер восстановления системы
Рис. 16 Контрольные точки восстановления системы
Оптимизация загрузки компьютера с помощью Планировщика заданий ОС MS Windows 10
Для оптимизации загрузки операционной системы рационально организовать отложенный запуск некоторых программ, которые по умолчанию прописаны в автозагрузке. При таком подходе сохраняется запуск программы, но ускоряется загрузка операционной системы.
При выборе программ, для которых необходимо установить отложенный запуск, нужны быть очень внимательными. Не стоит откладывать запуск антивирусных систем, различных драйверов устройств, а также программ, назначение которых неизвестно, чтобы это не повлияло на работоспособность операционной системы.
В данной статье я хочу для примера создать задачу в планировщике заданий для утилиты qBittorrent, которая может запускаться при старте операционной системы.
Будет рассмотрен вариант отложенного запуска утилиты qBittorrent на 4 минуты после входа в систему.
Рис. 17 Настройка автозагрузки приложений
Рис.18 Создание задачи с отложенным запуском в Планировщике заданий
Рис.19 Настройка задачи с отложенным запуском в Планировщике заданий
Рис.20 Создание триггера в настройках задачи с отложенным запуском в Планировщике заданий
Рис.21 Указание программы qBittorrent в окне Создание действия
Используя описанную технологию, можно автоматизировать процесс запуска и других задач для оптимизации работы операционной системы.
Как пользоваться планировщиком заданий в Windows 10
Если вы пользуетесь Windows 10, то автоматически получаете доступ к планировщику заданий, который позволяет упростить и ускорить работу с компьютером. Используя настройки, выполняемые с помощью этого сервиса, вы можете выставить задачи, которые будут запускаться при наступлении определенных условий. Это поможет оптимизировать процессы, протекающие в системе и нагружающие ее, а также добиться максимально комфортной работы системы.
Зачем нужен планировщик заданий
Планировщик заданий нужен в основном для двух дел: установки новых задач и удаления старых. Например, можно создать следующую задачу: запустить программу Skype, когда пользователь вошел в систему. Но можно сделать с точностью наоборот, если вы хотите, чтобы какая-нибудь программа не запускалась при входе в систему. Также, в планировщике задач можно найти многие процессы, запланированные самой системой, например, дефрагментация диска или обновление операционной системы.
Параметры запуска приложений
Можно выставить более тонкие условия, при которых будет запускаться та или иная программа:
Где находится и как отрыть
Есть несколько способов, при помощи которых можно запустить планировщик задач.
Как запустить через поиск Windows
Достаточно открыть поиск Windows, нажав на значок в виде лупы, и прописать в поисковой строке «Планировщик задач».
Планировщик Windows? Это очень просто
Введение
Реализация одной из ответственных задач моделирования в очередной раз привела к сложностям с операционной системой. Попытка решить задачу «под Windows», т.е. просто запустить программу, не применяя специальных средств, почти удалась, однако время от времени возникали недопустимые задержки. Эти, возникавшие случайно и редко (раз в несколько минут) задержки никак не удавалось убрать. Например, последовательное снятие всех «лишних» процессов Windows улучшало ситуацию, но, в конце концов, приводило к отказу самой ОС. Положение затрудняло и то, что проведение сравнительно долгого сеанса моделирования не позволяло на все 20-30 минут сеанса установить основному работающему потоку приоритет «реального времени», так как при этом нормальная работа компьютера также нарушалась.
Обидно было менять ОС и дорабатывать свое ПО, когда результат уже был почти достигнут и требовалось всего лишь изначально не предусмотренное в Windows планирование, а именно: заданный поток в течение определенного периода не должен прерываться по истечению выделенного кванта времени, и на время его работы потоки с более низким приоритетом вообще не должны получать управление. Но при этом потоки с изначально более высоким приоритетом должны выполняться как всегда. Поскольку такие высоко приоритетные потоки обычно не занимают весь свой квант времени, время отклика для нужного потока в целом уменьшается и зависит от быстродействия компьютера.
Встал вопрос: можно ли настроить Windows на такой режим работы и как это сделать?
Планирование потоков
Как известно, переключение на другой поток в Windows происходит в трех случаях:
истек выделенный квант времени работы и есть потоки с таким же приоритетом;
поток добровольно уступает время работы (например, начинает ждать события);
появился готовый к работе поток с более высоким приоритетом. Он немедленно (на самом деле в момент ближайшего прерывания) получает управление.
Кроме этого, в составе планировщика Windows имеется так называемый диспетчер баланса, который поднимает приоритет до значения 15 у давно ждущих выполнения потоков. Поскольку значения приоритетов класса «реального времени» начинаются с 16, то потоки «реального времени» диспетчер баланса прервать не может, а вот остальные потоки рано или поздно уступят квант потокам с более низким приоритетом. Скорее всего, это и является источником редких непредсказуемых задержек – иногда целый квант выполняется какой-то низкоприоритетный поток.
Проведем несложный эксперимент. Запустим одновременно две копии простейшей программы, которые просто выводят на экран постоянно увеличивающееся на единицу число. Чтобы не влияла многоядерность, назначим этим задачам одно и то же ядро процессора или вообще запустим компьютер в одноядерном режиме. Две программы, как и положено, работают «одновременно» (т.е. попеременно) и примерно с одинаковой скоростью. Теперь, используя диспетчер процессов, установим одной задаче приоритет «реального времени». Эта задача продолжает работать, а вторая задача останавливается. Все ожидаемо. Однако иногда и во второй задаче выдаваемое число увеличивается!
Объясните (как говорят в Одессе, «с указочкой»), почему задача с низким приоритетом вообще получает управление, когда по условиям эксперимента есть непрерывно работающая программа с заведомо очень большим приоритетом? Ответ на этот вопрос приведен в конце статьи и, честно говоря, он имеет мало отношения к теме.
Подобные эксперименты сеют сомнения: а все ли рассказали Руссинович и Соломон в своей книге [1] или, может быть, автор статьи просто что-то не так воспринял? Возникает еще один стимул изучить, а как работает Windows «на самом деле»? Документация документацией, но, как говорится, «это все слова – покажите код».
С другой стороны, тот же Руссинович сообщает, что Windows создавали 5000 программистов. Вряд ли одному человеку под силу разобраться во всех тонкостях такой большой и сложной системы. К счастью, разобраться требуется только в одной из многочисленных сторон ОС. А это вполне возможно и одному человеку и за сравнительно небольшое время.
Постановка задачи
Конкретизируем задачу. Требуется проанализировать код ядра Windows в части переключения с одного потока на другой. Нужно убедиться, что никаких других случаев переключения, кроме трех перечисленных, в ядре нет. Используя результаты анализа и знание конкретного кода, переключающего потоки, требуется организовать работу планировщика так, чтобы заданный поток в течение 20-30 минут не прерывался потоками с более низким приоритетом (из-за работы диспетчера баланса), но при этом не обладал приоритетом «реального времени» и поэтому не мешал различным высокоприоритетным служебным потокам и сервисам.
Получение кода для анализа
К сожалению, получить текст кода ядра для анализа не так-то просто. Т.е. ядро ntoskrnl.exe невозможно просто загрузить в память с помощью какого-нибудь ntsd или windbg. Конечно, есть и специальные средства, и отладочные версии, и виртуальные машины, но в данном случае хотелось бы получить просто ассемблерный код как текст, который можно даже хотя бы частично распечатать и спокойно анализировать «за столом». Для этой цели проще создать небольшую программу (я назвал ее sd.exe) самому. Поскольку в используемых мною средствах есть встроенный отладчик, легко написать небольшую программу, просто загружающую файл ntoskrnl в память и затем сдвигающую на нужную величину каждую секцию, перечисленную в таблице заголовка exe-файла. Выполнив эти действия, программа останавливается в контрольной точке (т.е. на команде INT 3). В результате в памяти получается правильно «развернутый» образ ядра из ntoskrnl, который теперь можно вывести на экран или в файл командами «U» и «D» встроенного интерактивного отладчика. Сложность такого дизассемблирования в том, что команды и данные идут вперемежку, и если весь файл вывести как команды, данные выведутся как набор бессмысленных команд, часто портящих начало участков настоящих команд. Приходится предварительно все просматривать на экране как данные и на глаз определять очередные границы команд и данных. Результаты просмотра оформляются в виде текста как последовательность команд «U» и «D» для будущего получения «распечатки»:
Здесь все адреса указаны относительно регистра EAX, в который в программе sd.exe записывается адрес загрузки файла ntoskrnl в памяти. Иногда удобнее вместо команды «D» использовать также имеющуюся в данном отладчике команду «DD», выводящую данные двойными словами, т.е. адресами. Например, вот адреса рассылки по прерываниям INT 00, INT 01, INT 02,…:
Кстати, найденный адрес 409150 исключения INT 0D «нарушение общей защиты» еще пригодится далее.
Теперь если отладчик выполнит последовательность команд «U» и «D», получается текст вот такого, уже более правильного вида:
Таким образом, команды отделяются от данных. Всю последовательность команд для отладчика я записал в файл ud.txt и одной командой:
sd.exe ntoskrnl.exe ntos.txt
получил первый вариант кода ядра в текстовом файле ntos.txt. Этот вариант еще достаточно «слепой». Однако теперь уже несложно создать еще одну небольшую программу, которая обработает полученный результат, добавляя в текст названия импортируемых процедур, используя таблицу импорта исходного exe-файла, а также расставит метки по тексту, используя адреса таблицы экспортируемых функций. Кроме этого, программа вставляет всякие удобные мелочи вроде пустой строки после каждой команды RET, чтобы легче читать анализируемые участки и т.д.
На основе «исходного» ассемблерного текста получается обработанный текст, уже больше подходящий для анализа. В «исходный» текст можно вручную вносить правки, например, комментарии, пустые строки и т.п., после чего очередной раз обрабатывать программой и получать с каждой итерацией все более и более понятный текст кода ядра, по мере накопления в нем комментариев. Кроме этого, в обрабатывающую программу можно добавлять проверки на определенный контекст и автоматически расставлять некоторые комментарии. В результате анализируемый текст становится все менее и менее «слепым», например:
Самое главное, что теперь в этом большом (26 Мбайт) текстовом файле легко искать нужный контекст, например, переход на заданный адрес. А значит, можно приступать собственно к анализу кода ядра.
Анализ кода
По условиям задачи анализировать потребовалось ядро Windows-XP SP3 сборки 0421 от 4 июля 2013 года. При этом в очень большом тексте (примерно 570 000 ассемблерных строк) нужно было по возможности быстро найти элементы планировщика, отвечающие за переключение потоков.
С чего начать? Очевидно с поиска «сердца» ОС – т.е. с процедуры, вызываемой при каждом аппаратном срабатывании сигнала встроенных часов. Это просто, ведь есть экспортируемое имя KeUpdateSystemTime и его адрес 40B558 (далее комментарии в тексте частью расставлены программой, частью дописаны вручную):
Далее идет обновление числа «тиков» и проверки таймеров, а затем самый важный для анализа фрагмент:
Т.е. при каждом окончании времени кванта запускается подпрограмма с вполне соответствующим случаю названием KeUpdateRunTime.
Она расположена по тексту рядом:
Откуда следует, что это достаются именно текущий поток и процесс?
Это легко выясняется, например, из процедуры KeGetCurrentThread:
И из процедуры IoGetCurrentProcess:
Самое интересное место расположено в конце KeUpdateRunTime:
Здесь из некоторого поля внутренней структуры текущего потока со смещением 6F вычитается 3 и, если этот счетчик становится не положительным, в некоторую переменную с относительным адресом 9AC заносится зачем-то значение ESP. А где используется такая переменная? Оказывается, контекстный поиск смещения 9AC находит одно единственное место внутри KiDispatchInterrupt:
Если значение переменной 9AC не равно нулю, оно сбрасывается, затем идет обращение к некоторой процедуре по адресу 411ABF. И если процедура возвращает ненулевой EAX, то управление попадает на адрес 4058D1. А здесь это значение (командой по адресу 4058F1) пишется как новый текущий поток. Вот нужное место и найдено!
Теперь понятна вся цепочка действий ядра: на каждый «тик» встроенных часов запускается KeUpdateSystemTime, где текущий квант уменьшается на число прошедших «тиков». Если квант истек, запускается KeUpdateRunTime, которая уменьшает внутренний счетчик в структуре текущего потока. Как только этот счетчик истекает, данное событие отмечается в переменной с относительным адресом 9AC. При ближайшем прерывании запускается KiDispatchInterrupt, которая проверяет переменную 9AC. Если переменная не нулевая (именно для этого в нее занесли ESP) – значит время данного потока исчерпано.
С помощью подпрограммы по адресу 411ABF ОС ищет новый поток для работы. Если конкурента текущему потоку не находится, он продолжает выполнение. Иначе текущий поток переводится в режим ожидания с помощью процедуры по адресу 405667, и запускается (т.е. становится текущим) другой поток.
Интересно, что внутри процедуры с адресом 411ABF проверяется, равно ли нулю поле 69 структуры текущего потока. Если нет – новый поток не ищется. Это поле описано в документации как DisableQuantum. Т.е. квант работы можно сделать бесконечным!
Увы, установить это поле из режима пользователя нельзя. Сама ОС может установить любое значение этого поля с помощью внутренней процедуры по адресу 43CA4B. Однако когда она использует эту подпрограмму, всегда данное поле устанавливается в ноль. Жаль, было бы удобно с помощью какого-нибудь недокументированного сервиса задать себе таким способом «бесконечный» квант работы.
А есть ли еще места смены текущего потока? Да, есть, и они по тексту рядом.
Это подпрограмма вызывается, например, внутри KeWaitForMultipleObjects. Очевидно, что это случай «добровольной» смены потока при ожиданиях, задержках, окончании задачи и т.п.
Наконец, еще одно место изменения поля со смещением 124 находится контекстным поиском чуть выше:
Здесь уже не проверяется переменная 9AC, но опять проверяется наличие потока с более высоким приоритетом. Очевидно, что это обработка прерывания, случившегося внутри самой ОС, где квант не может истечь по определению, но на появление готового потока с более высоким приоритетом нужно реагировать немедленно.
И это весь анализ по части смены потока. Контекстным поиском больше не найдено мест, где бы менялся текущий поток (по смещению 124). А значит, анализировать остальные сотни тысяч строк ассемблерного кода уже нет никакой необходимости. ОС именно так как описано в документации меняет текущий поток или по исчерпанию заданного числа квантов (что определяется счетчиком в поле 6F структуры текущего потока) или при появлении более высокоприоритетного или если поток сам уступает время выполнения. Других «секретных» способов не обнаружено. Для решения поставленной задачи осталось лишь понять работу диспетчера баланса. Кстати, где он?
Диспетчер баланса использует понятие «старения» ждущих потоков. Значит, он должен достать текущий «тик» (переменная по адресу [483000], меняющаяся только внутри KeUpdateSystemTime), затем отнять из него некоторую константу и полученное значение сравнивать со временем перевода данного потока в режим ожидания. Это время должно храниться где-то в структуре каждого ждущего потока. Несложно найти в тексте все вычитания из системного «тика». Например, вот место доставания текущего времени и вычитание из него константы 300:
Если это и есть диспетчер баланса, тогда вот в нем сама проверка степени «старения» потока по времени его ожидания в поле со смещением 68:
А вот и нашлось поднимание текущего приоритета до 15, а также указанное в документации удвоение времени работы в кванте в этом случае:
Теперь, наконец, анализ кода можно считать оконченным. В более чем полумиллионе ассемблерных строк с помощью контекстного поиска и общих соображений достаточно легко нашлось несколько десятков команд полностью и в строгом соответствии с документацией объясняющих поведение ОС при переключении потоков.
Изменение поведения планировщика
Теперь мы вооружены знаниями о том, как на уровне кодов происходит смена потока в Windows. Но как заставить планировщик работать в соответствии с поставленной задачей? Т.е., во-первых, сделать квант «бесконечным» на время работы заданного потока, а во-вторых, не допустить, чтобы диспетчер баланса поднял приоритет давно ждущих потоков выше приоритета заданного потока.
Для этого требуется внести исправление в само ядро. Это не так уж и сложно. Конечно, потребуется позаботиться о пересчете контрольной суммы с помощью процедуры CheckSumMappedFile и тому подобных мелочах, но это не является серьезным препятствием. Самое главное – организовать удобный интерфейс задачи пользователя с ядром. Напоминаем, что это делается для единственного компьютера.
Была выбрана схема, при которой запущенный поток сам периодически сообщает ядру о своей «избранности». При получении этого сообщения ядро продлевает квант выполнения и ограничивает подъем приоритетов диспетчером баланса не выше заданного. Как только (минут через 20-30) поток завершается, он перестает давать сообщения ядру. Поэтому ОС опять начинает выполнять фрагмент кода по исчерпанию кванта (для других потоков). В этом месте будет срабатывать возврат диспетчера баланса в нормальный режим работы. Таким образом, после завершения нужного потока ядро автоматически возвращается в обычный режим работы.
Сначала нужно найти место для размещения дополнительных команд. Таких мест много, например, команды можно написать вместо вот этого длинного диагностического текста, который вряд ли когда потребуется:
Передача сообщения ядру происходит с помощью выполнения привилегированной команды, на которую сработает исключение INT 0D «нарушение общей защиты». При этом предварительно в одном из регистров пишется специальное значение, которое и позволит ядру отличить этот случай от всех остальных. Кстати, само ядро тоже пользуется похожим приемом, например, в интерфейсе запроса времени INT 2A в регистре EBP можно записать специальные значения F0F0F0F0 или F0F0F0F1, которые заставят ядро реагировать на INT 2A по-разному.
Для начала команды обработчика исключения INT 0D в ядре по адресу 409150 можно немного «уплотнить» и добавить вызов новой подпрограммы (размещенной по адресу 5553A0 на месте текста), не двигая остальной код обработчика:
Как видите, при необходимости даже оптимизированный код можно «ужать» и вставить дополнительные команды.
А на место диагностического текста помещаются основные команды исправления ядра:
Дополнительный обработчик исключения проверяет сигнатуру ESI=55554444 и выполняет следующие действия:
— устанавливает максимальное значение счетчика 127 для текущего потока;
— достает приоритет текущего потока и вставляет его как константу прямо внутрь команды, через которую проходит управление в диспетчере баланса. Чтобы найти относительный адрес исправляемой команды, выполняется фиктивный вызов процедуры;
— пропускает команду, которая вызвала это исключение, выбрасывает из стека адрес возврата и код ошибки и возвращается прямо в задачу пользователя.
По сути, Windows вообще не «чувствует» такое исключение, поскольку управление сразу же возвращается в задачу, минуя обычные пути обработки исключений. В программе достаточно хотя бы раз в 2-3 секунды давать исключение с таким значением в ESI и тогда внутренний счетчик потока по адресу 6F никогда не достигнет нуля. А значит, переменная 9AC продолжает оставаться нулевой и Windows не ищет замену текущему потоку.
Остается поправить диспетчер баланса. В него добавляются команды, проверяющие приоритет ждущего потока. Если приоритет ниже, диспетчер действует так, как будто поток еще не «старый»:
Первоначально приоритет сравнивается с константой 16, которой у проверяемых потоков не может быть, и поэтому проверка никак не влияет на обычную работу диспетчера. Но когда начинают приходить сообщения от «избранного» потока, константа 16 прямо в команде проверки заменяется значением приоритета заданного потока. Теперь всем более низкоприоритетным потокам диспетчер уже не пытается поставить приоритет 15.
Требуется лишь вернуть константу 16 на место после того, как заданный поток закончился. В этом случае ОС опять начинает выполнять поиск потоков по исчерпанию кванта, в это место и можно добавить команды восстановления:
Все перечисленные вставки кодов записаны непрерывно на месте диагностического текста и разделены здесь лишь для более наглядного пояснения их работы.
Доработка прикладного ПО
Анализ и исправление ядра ОС занял примерно неделю, зато все прикладное ПО осталось без изменения, за исключением добавления в одном месте в главный цикл основного модуля подпрограммы выдачи сообщения ядру.
На языке PL/1 подпрограмма выдачи сообщения ядру выглядит так:
Достаточно хотя бы раз в 2-3 секунды (т.е. пока не истечет внутренний счетчик, нужно опять успеть присвоить ему максимальное значение) обращаться из задачи пользователя к этой процедуре, как данный поток будет работать, не прерываясь на целые кванты для менее приоритетных потоков.
Заключение
Может показаться, что затрачено непропорционально много сил и времени лишь на то, чтобы заставить Windows работать неправильно. Но это не так. Поведение ОС не должно быть незыблемой данностью, указанной свыше. Есть большое число случаев, когда программа предназначена не для массовой работы на любых компьютерах (разумеется, тогда исправление ОС невозможно), а на конкретной машине и конкретной версии ОС.
Объективно универсальная ОС не может одинаково хорошо работать во всех мыслимых случаях. В данном случае разработчики ОС не могли предполагать, что одному потоку потребуется какое-то особое планирование. Ведь Windows пытается не допустить случая, когда поток вообще никогда не получит управления. Собственно, именно эти архитектурные особенности и не позволяют назвать эту ОС системой реального времени.
Однако данный пример показывает, что даже основные архитектурные особенности самой распространенной в мире ОС могут быть перенастроены для конкретного случая, превращая ее практически в систему реального времени. И при этом не требуется доскональное изучение всей ОС (да это и нереально), достаточно, исходя из логики и общих соображений, исследовать несколько подходящих мест. Для этого понадобилась лишь пара простых программ и текстовый редактор с контекстным поиском. Подобной технологией можно создать свою версию практически для любого случая без радикальной переделки всей системы.
И с юридической точки зрения это допустимо. Например, статья 6 Директивы 2009/24/EC или статья 25 Закона РФ об авторском праве [2] разрешают адаптацию программ для функционирования на технических средствах покупателя программы.
Здесь как раз тот самый случай, когда декомпиляция и исправления приводят к улучшению функциональности, которое выразилось в уменьшении одной из важных характеристик ОС – времени отклика, так как планировщик теперь не прерывает текущий поток на целые кванты для низкоприоритетных потоков. Но никакого чуда не произошло. Улучшение работы одного потока обусловлено временной остановкой остальных, что, разумеется, не может быть допустимым во всех случаях.
P.S. А тогда почему пример с двумя задачами так странно работал, раз в ядре не нашлось недокументированного планирования? Все просто. Сам пример был в данном случае некорректен. Ведь каждая из программ выдавала значение на экран обращением к стандартному файлу консольного вывода, причем в синхронном режиме. Т.е. наступал момент, когда задача с приоритетом «реального времени» просто уступала свое время, дожидаясь окончания выдачи на экран. В этот момент планировщик запускал поток с низким приоритетом, который успевал сменить значение переменной и сам уступал из-за выдачи на экран свое время, что вызывало возобновление работы высокоприоритетной задачи. Если отменить выдачу на экран, зацикленная задача приоритета «реального времени» просто «подвесит» весь компьютер (ну, или ядро на многоядерном процессоре), о чем и предупреждает документация.
Литература
1. М. Руссинович, Д. Соломон Внутреннее устройство Microsoft Windows, Windows Server™ 2003, Windows XP и Windows 2000 4-е издание