Программирование служб Windows 7 с триггерами (ч.1)

Автор: Topol Четверг, Май 3rd, 2012 Нет комментариев

Рубрика: Операционные системы

Несколько недель назад мы рассмотрели изоляцию Сессии 0 в свете программной совместимости. Поэтому вполне естественно, что мы возвращаемся к обсуждению служб в контексте Windows 7. Но на этот раз мы поговорим о некоторых выгодах оптимизации служб, доступных в Windows 7. Эта статья посвящена новой возможности Windows 7  - Trigger Start Services. Но прежде, чем мы обратимся к API, давайте обрисуем общий фон служб.

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

Мы предпочитаем считать службы запущенными задачами, работающими в фоновом режиме и не затрагивающими операции пользователя. Службы в Windows отвечают за все виды фоновой активности, начиная со службы Remote Procedure Call (RPC), Printer Spooler и вплоть до службы Network Location Awareness.

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

Что плохого в службах, работающих 24 часа в сутки 7 дней в неделю?
Есть несколько проблем, связанных с наличием служб, работающих по схеме 24х7. Во-первых, зачем что-то должно работать (пускай даже и в фоновом режиме), если в его работе нет нужды? Любой запущенный процесс (включая службы) использует драгоценную память и ресурсы ЦП, которые могли бы использоваться для других приложений и служб. Если вы подсчитаете все службы, запущенные в определенный момент, то они сложатся в значительный объем памяти, дескрипторов, потоков и использование ЦП. Все эти «растрачиваемые» ресурсы понижают общую производительность компьютера, его отзывчивость и заставляют пользователя думать, что его компьютеры вялые и медлительные. К тому же, поскольку множество работающих служб настроены на автоматический запуск (начинают работать со входом в систему), эти службы влияют на время загрузки компьютера.

Во-вторых, эти растрачиваемые ресурсы непосредственным образом сказываются на потреблении электроэнергии. Чем больше нагрузки мы даем на ЦП, тем больше электроэнергии компьютер потребляет. Это может быть критически важно для ноутбуков и может сократить время работы батареи на несколько часов.

В-третьих, постоянная работа непродуктивного программного обеспечения может привести к утечкам памяти и общей нестабильности системы. Это может привести к сбою в работе приложения и, в конце концов, компьютера.

Наконец, если служба работает по схеме 24х7, и если это хорошо известная служба (которая может оказаться у каждого популярного приложения — например, у PDF Reader), то это создает большую поверхность для атаки. Злоумышленник может воспользоваться сведениями о том, что определенное популярное приложение устанавливает службу, работающую в режиме 24х7, и попытаться взломать ее для получения доступа к компьютеру.

Учитывая все вышесказанное, вы можете удивиться, почему так много разработчиков настраивают свои службы на постоянную работу, если у них имеется другая возможность. Даже до Windows 7 было доступно несколько вариантов запуска служб:

  • Disabled (Отключена) полностью отключает службу и предотвращает ее запуск и запуск зависимых служб — это означает, что пользователь должен включить службу вручную из Панели управления или командной строки
  • Manual (Вручную) запускает службу по надобности (в связи с зависимостями других служб) или при вызове службы из приложения при помощи соответствующих API, как будет показано ниже
  • Automatic (Автоматически) запускает службу при входе в систему
  • Automatic Delayed (Автоматически (отложенный запуск) - более новый тип запуска, появившийся в Windows Vista, при помощи которого запуск службы происходит после завершения загрузки и выполнения первоначальных операций, что ускоряет запуск системы

К сожалению, многие ISV (включая саму Microsoft) продолжают настраивать свои службы на автоматический (Automated) или автоматический отложенный запуск (Automatic Delayed), поскольку для всех это представляется простейшим решением. Служба просто работает 24х7 и всегда доступна, устраняя любую необходимость проверки зависимостей или того, запущена ли служба.

Можно привести множество примеров существующих служб, которые могут расходовать куда меньше ресурсов и стать безопаснее, не работая 24х7. Например, подумайте о службе обновлений, которая проверяет наличие новых обновлений для приложения. Если компьютер не подключен к сети и не имеет IP-адреса, зачем работать этой службе? Она ничего не может сделать, так зачем оставлять работающей программу, которая ничего не делает? Подумайте о службе управления политиками, которая используется при изменении групповых политик или при подключении компьютера к домену или отключении от него, но сейчас, когда компьютер подключен к моей домашней сети, служба, опять же, работает впустую.

Появление служб с запуском по триггеру
Решение вышеуказанных проблем заключается в выведении службы из «состояния постоянной работы» в другие виды фоновой активности, такие как запланированные задачи или службы, запускаемые триггером. Эта статья посвящена Windows 7 Trigger Start Services. О Windows 7 Scheduled Tasks можно сказать очень много полезного, что и будет сделано в последующих статьях.

[url=http://msdn.microsoft.com/en-us/library/dd405513(VS.85).aspx]Службы, запускаемые по триггеру[/url] (англ. trigger-start service), впервые появились в Windows 7. По сути, это обычная служба, которую вы можете настроить на запуск (или остановку) в случае срабатывания триггера, то есть в определенном случае или состоянии, которые вы сами задаете (например, когда становится доступным IP-адрес или когда он исчезает). Ниже приведен список доступных триггеров, с помощью которых вы можете настроить режим запуска вашей службы:

  • Подключение или отключение устройства
  • Вход в домен или выход из него
  • Открытие или закрытия порта брандмауэра
  • Изменение в групповых политиках
  • Доступность первого IP-адреса / исчезновение последнего IP-адреса
  • Настраиваемое событие — Трассировка событий для Windows (ETW)

Последний пункт указывает на расширяемость. Будучи разработчиком, вы можете настроить любое событие ETW в качестве триггера для службы, что дает вам очень хороший инструмент для управления запуском и остановкой служб вашего приложения.

Так что же такое триггер?
Триггер состоит из:

  • Типа события триггера
  • Подтипа события триггера
  • Действия, которое должно быть предпринято при совершении события триггера
  • Одного или более элементов данных, связанных с триггером (для определенных типов событий триггера)

Подтип и связанные с триггером элементы данных вместе устанавливают состояние для уведомления службы о событии. Формат элемента данных зависит от типа события триггера; элемент данных может состоять из бинарных, строковых или многостроковых данных.

Работа с Trigger Start Services
К сожалению, в пользовательском интерфейсе консоли Windows 7 Services MMC нет графического представления Trigger Start Services. Однако у вас есть две возможности. Вы можете по-прежнему использовать старый добрый sc.exe (программа командной строки Service Configuration) или воспользоваться методом WIN32 ChangeServiceConfig2 для программной настройки опций запуска службы, как будет показано в этой статье.

Использование SC.exe для запроса данных триггера службы (Query Service Trigger Information)
Пора повеселиться. Начнем с получения сведений о конфигурации некоторых служб. Общая форма для использования конфигурации службы выглядит следующим образом:

Код:
sc <server> [command] [service name] <option1> <option2>…

Где server опционален, а по умолчанию вы работаете с локальным компьютером:

  • command - это операция, которую вы хотите выполнить, например, запрос данных триггера
  • service name - это имя службы, с которой вы хотите работать
  • options - это различные значения (опции), которые вы можете выполнить для настройки службы

Начнем с запроса определенной службы о ее конфигурации триггера. Для этого нам понадобится запустить окно Windows Shell:

[ist]1. Откройте меню «Пуск».
2. Введите CMD в поле поиска.
3. Выберите cmd.exe.
4. Введите sc qtriggerinfo w32time и нажмите клавишу ввода.
Вот, как это должно выглядеть:

Как вы можете видеть, мы запросили данные триггера службы W32time, которая настроена на запуск при подключении компьютера к домену и остановку при отключении от домена.

Microsoft в Windows 7 обновила приложение командной строки sc.exe для поддержки конфигурации и получения сведений о поддерживаемых триггерах. Введите sc triggerinfo в окне Windows Shell и нажмите клавишу ввода. Результат будет похож на тот, что приведен ниже, и будет содержать все триггеры и сведения о том, как настроить службы на их использование.

Код:
ОПИСАНИЕ:
Изменяет параметры активации службы.
USAGE:
sc <сервер> triggerinfo [имя службы] <параметр1> <параметр2>…

ПАРАМЕТРЫ:
start/device/UUID/HwId1/… <Запуск службы после получения строки UUID указанного класса интерфейса устройства с одной или несколькими строками кода оборудования или совместимыми строками кода>
start/custom/UUID/data0/.. <Запуск службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими двоичными элементами данных в формате шестнадцатеричной строки, например,r ABCDABCD, для задания 4 байтов данных>
stop/custom/UUID/data0/… <Остановка службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими двоичными элементами данных в формате шестнадцатеричной строки, например, ABCDABCD, для задания 4 байтов данных>
start/strcustom/UUID/data0/.. <Запуск службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими необязательными элементами данных>
stop/strcustom/UUID/data0/.. <Остановка службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими необязательными элементами данных>
start/networkon <Запуск службы при первом IP-адресе>
stop/networkoff <Остановить службу при отсутствии IP-адресов>
start/domainjoin <Запуск службы при подключении к домену>
stop/domainleave <Остановка службы при отсоединении от домена>
start/portopen/параметр <Запуск службы при открытии сетевого порта. Параметр имеет следующую форму: номер_порта;имя_протокола; путь_к_образу;имя_службы>
stop/portclose/параметр <Остановка службы при закрытии сетевого порта. Параметр имеет следующую форму: номер_порта;имя_протокола; путь_к_образу;имя_службы>
start/machinepolicy <Запуск службы при изменении групповой политики компьютера или при ее наличии на момент загрузки>
start/userpolicy <Запуск службы при изменении групповой политики пользователя или при ее наличии на момент загрузки>
delete <Удаление текущих параметров активации>

Так всё, что вам нужно для настройки службы на запуск при появлении IP-адреса, — это ввести sc triggerinfo [имя вашей службы] start/networkon, где «имя вашей службы» заменено на имя той службы, которую вы хотите настроить.

Программная настройка Trigger Start Services при помощи ChanceServiceConfig2
Более интересным с точки зрения разработчиков аспектом является создание служб, зависящих от триггера, и использование кода для конфигурации службы. В Windows 7 вы можете использовать функцию ChangeServiceConfig2 для настройки данных триггера службы и функцию QueryServiceConfig2 для их вызова.

Регистрация триггера службы производится вызовом ChangeServiceConfig2 использованием SERVICE_CONFIG_TRIGGER_INFO для параметраdwInfoLevel и представлением данных регистрации триггера в структуре SERVICE_TRIGGER_INFO посредством параметра lpInfo. К тому же, могут быть указаны дополнительные связанные с триггером данные. Ниже приведен пример функции установщика службы, который создает триггер USB-устройства для службы под названием MyService:

Код:
define SERVICE_NAME L»MyService»
//set the device guid
static const GUID GUID_USBDevice = {
0x53f56307, 0xb6bf, 0x11d0,
{0×94, 0xf2, 0×00, 0xa0, 0xc9,
0x1e, 0xfb, 0x8b }};

BOOL _SetServiceToStartOnDeviceTrigger()
{
BOOL fResult = FALSE;

SC_HANDLE hScm = OpenSCManager(
NULL, //local machine
NULL, //active database
SC_MANAGER_CONNECT);

if(hScm != NULL)
{
SC_HANDLE hService = OpenService(
hScm,
SERVICE_NAME,
SERVICE_ALL_ACCESS);

If( hService != NULL)
{

LPCWSTR lpszDeviceString = L»USBSTOR\\GenDisk»;
SERVICE_TRIGGER_SPECIFIC_DATA_ITEM deviceData = {0};
deviceData.dwDataType = SERVICE_TRIGGER_DATA_TYPE_STRING;
deviceData.cbData =
(wcslen(lpszDeviceString)+1) * sizeof(WCHAR);
deviceData.pData = (PBYTE)lpszDeviceString;

SERVICE_TRIGGER st;
st.dwTriggerType =
SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL;
st.dwAction = SERVICE_TRIGGER_ACTION_SERVICE_START;
st.pTriggerSubtype = (GUID *) &GUID_USBDevice;
st.cDataItems = 1;
st.pDataItems = &deviceData;

SERVICE_TRIGGER_INFO sti;
sti.cTriggers = 1;
sti.pTriggers = &st;
sti.pReserved = 0;

fResult = ChangeServiceConfig2(
hService,
SERVICE_CONFIG_TRIGGER_INFO,
&sti);
}
CloseServiceHandle (hService);
}
CloseServiceHandle (hScm);

if(!fResult)
{
printf(«Service trigger registration failed (%d)\n»,
GetLastError());
}
return fResult;
}

Примечание: все службы контролируются Service Control Manager (SCM), который мы рассмотрим в другой статье.

Вы можете видеть, как в приведенном выше фрагменте кода мы сначала берем дескриптор (hScm) к SCM вызовом openSCManager. Далее мы вызываем openService и проходим дескриптор к SCM — hscm, и имя службы — SERVICE_NAME, к которой мы хотим получить доступ. Последний параметр, SERVICE_ALL_ACCESS, указывает, что у нас имеется полный доступ к службам. Полагая, что теперь у нас в службе имеется верный дескриптор, мы начинаем создавать отдельную структуру, которой воспользуемся вскоре для настройки службы.

SERVICE_TRIGGER_SPECIFIC_DATA_ITEM задает тип события триггера. Он содержит данные о событии триггера службы. В нашем случае, мы задаем строку, описывающую подключение USB-диска.

Затем мы задаем структуру SERVICE_TRIGGER, которая представляет события триггеру службы. Заметьте, что именно здесь мы задаем тип триггера (подключение устройства), действие (запуск службы), и подтип триггера (определенный род USB-дисков). Следом мы определяем конкретное устройство, которое будет вызывать службу. Заметьте, что вы можете задать список устройств и их GUID. Также следует отметить, что мы не хотим срабатывания триггера запуска службы при подключении любого USB-устройства, как то мышь или камера. Мы хотим, чтобы служба запускалась только при появлении USB-диска.

Наконец, мы задаем структуру SERVICE_TRIGGER_INFO, которая содержит данные события триггера службы. Эта структура просто указывает на структуру SERVICE_TRIGGER, которую мы задали ранее, и количество триггеров, число которых в данном случае равно одному.

Теперь мы можем вызвать функцию ChanceServiceConfig2 и пройти дескриптор к службе, которую мы хотим настроить, параметр SERVICE_CONFIG_TRIGGER_INFO, который указывает, что мы хотим настроить триггер службы, и Null.

Вот и все. Если вы все сделали правильно, то служба запуститься при подключении USB жесткого диска.

В следующей статье я рассмотрю, как написать простую реализацию службы .NET, которую мы настроим на запуск при подключении диска USB.

Источник: thevista.ru

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты