[ главная ]   [ рейтинг статей ]   [ справочник радиолюбителя ]   [ новости мира ИТ ]



Ответов: 0
25-02-12 07:01







   Web - программирование
PHP


ASP






XML



CSS

SSI





   Программирование под ОС











   Web - технологии








   Базы Данных









   Графика






Данные




Операционные системы / Windows /

Службы Windows NT. Назначение и разработка

Михаил Плакунов, СофтТерра

Службы Windows NT, общие понятия


Служба Windows NT (Windows NT service) — специальный процесс, обладающий унифицированным интерфейсом для взаимодействия с операционной системой Windows NT. Службы делятся на два типа — службы Win32, взаимодействующие с операционной системой посредством диспетчера управления службами (Service Control Manager — SCM), и драйвера, работающие по протоколу драйвера устройства Windows NT. Далее в этой статье мы будем обсуждать только службы Win32.

Применение служб


Одним из важнейших свойств службы является неинтерактивность. Типичное «поведение» службы — это незаметная для обычного пользователя работа в фоновом режиме. В силу этого службы наиболее подходят для реализации следующих типов приложений:

  • Сервера в архитектуре клиент-сервер (например, MS SQL, MS Exchange Server)
  • Сетевые службы Windows NT (Server, Workstation);
  • Серверные (в смысле функциональности) компоненты распределенных приложений (например, всевозможные программы мониторинга).

Основные свойства служб


От обычного приложения Win32 службу отличают 3 основных свойства. Рассмотрим каждое из них.

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

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

И, наконец, возможность работы в произвольном контексте безопасности. Контекст безопасности Windows NT определяет совокупность прав доступа процесса к различным объектам системы и данным. В отличие от обычного приложения Win32, которое всегда запускается в контексте безопасности пользователя, зарегистрированного в данный момент в системе, для службы контекст безопасности ее выполнения можно определить заранее. Это означает, что для службы можно определить набор ее прав доступа к объектам системы заранее и тем самым ограничить сферу ее деятельности. Применительно к службам существует специальный вид контекста безопасности, используемый по умолчанию и называющийся Local System. Служба, запущенная в этом контексте, обладает правами только на ресурсы локального компьютера. Никакие сетевые операции не могут быть осуществлены с правами Local System, поскольку этот контекст имеет смысл только на локальном компьютере и не опознается другими компьютерами сети.

Взаимодействие службы с другими приложениями


Любое приложение, имеющее соответствующие права, может взаимодействовать со службой. Взаимодействие, в первую очередь, подразумевает изменение состояния службы, то есть перевод ее в одно из трех состояний — работающее (Запуск), приостанов (Пауза), останов и осуществляется при помощи подачи запросов SCM. Запросы бывают трех типов — сообщения от служб (фиксация их состояний), запросы, связанные с изменением конфигурации службы или получением информации о ней и запросы приложений на изменение состояния службы.

Для управления службой необходимо в первую очередь получают ее дескриптор с помощью функции Win32 API OpenService. Функция StartService запускает службу. При необходимости изменение состояния службы производится вызовом функции ControlService.

База данных службы


Информация о каждой службе хранится в реестре — в ключе HKLMSYSTEMCurrentControlSetServicesServiceName. Там содержатся следующие сведения:

  • Тип службы. Указывает на то, реализована ли в данном приложении только одна служба (эксклюзивная) или же их в приложении несколько. Эксклюзивная служба может работать в любом контексте безопасности. Несколько служб внутри одного приложения могут работать только в контексте LocalSystem.
  • Тип запуска. Автоматический — служба запускается при старте системы. По требованию — служба запускается пользователем вручную. Деактивированный — служба не может быть запущена.
  • Имя исполняемого модуля (EXE-файл).
  • Порядок запуска по отношению к другим службам. В некоторых случаях для корректной работы службы требуется, чтобы была запущена одна или несколько других служб. В этом случае в реестре содержится информация о службах, запускаемых перед данной.
  • Контекст безопасности выполнения службы (сетевое имя и пароль). По умолчанию контекст безопасности соответствует LocalSystem.

Приложения, которым требуется получить информацию о какой-либо службе или изменить тот или иной параметр службы, по сути должны изменить информацию в базе данных службы в реестре. Это можно сделать посредством соответствующих функций Win32 API:

  • OpenSCManager, CreateService, OpenService, CloseServiceHandle — для создания (открытия) службы;
  • QueryServiceConfig, QueryServiceObjectSecurity, EnumDependentServices, EnumServicesStatus — для получения информации о службе;
  • ChangeServiceConfig, SetServiceObjectSecurity, LockServiceDatabase, UnlockServiceDatabase, QueryServiceLockStatus — для изменения конфигурационной информации службы.

Внутреннее устройство службы.


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

Функция main


Как известно функция main — точка входа любого консольного Win32 приложения. При запуске службы первым делом начинает выполняться код этой функции. Втечение 30 секунд с момента старта функция main должна обязательно вызвать StartServiceCtrlDispatcher для установления соединения между приложением и SCM. Все коммуникации между любой службой данного приложения и SCM осуществляются внутри функции StartServiceCtrlDispatcher, которая завершает работу только после остановки всех служб в приложении.

Функция ServiceMain


Помимо общепроцессной точки входа существует еще отдельная точка входа для каждой из служб, реализованных в приложении. Имена функций, являющихся точками входа служб (для простоты назовем их всех одинаково — ServiceMain), передаются SCM в одном из параметров при вызове StartServiceCtrlDispatcher. При запуске каждой службы для выполнения ServiceMain создается отдельный поток.

Получив управление, ServiceMain первым делом должна зарегистрировать обработчик запросов к службе, функцию Handler, свою для каждой из служб в приложении. После этого в ServiceMain обычно следуют какие-либо действия для инициализации службы — выделение памяти, чтение данных и т.п. Эти действия должны обязательно сопровождаться уведомлениями SCM о том, что служба все еще находится в процессе старта и никаких сбоев не произошло. Уведомления посылаются при помощи вызовов функции SetServiceStatus. Все вызовы, кроме самого последнего должны быть с параметром SERVICE_START_PENDING, а самый последний — с параметром SERVICE_RUNNING. Периодичность вызовов определяется разработчиком службы, исходя их следующего условия: продолжительность временного интервала между двумя соседними вызовами SetServiceStatus не должна превышать значения параметра dwWaitHint, переданного SCM при первом из двух вызовов. В противном случае SCM, не получив во-время очередного уведомления, принудительно остановит службу. Такой способ позволяет избежать ситуации «зависания» службы на старте в результате возникновения тех или иных сбоев (вспомним, что службы обычно неинтерактивны и могут запускаться в отсутствие пользователя). Обычная практика заключается в том, что после завершения очередного шага инициализации происходит уведомление SCM.

Функция Handler


Как уже упоминалось выше, Handler — это прототип callback-функции, обработчика запросов к службе, своей для каждой службы в приложении. Handler вызывается, когда службе приходит запрос (запуск, приостанов, возобновление, останов, сообщение текущего состояния) и выполняет необходимые в соответствии с запросом действия, после чего сообщает новое состояние SCM.

Один запрос следует отметить особо — запрос, поступающий при завершении работы системы (Shutdown). Этот запрос сигнализирует о необходимости выполнить деинициализацию и завершиться. Microsoft утверждает, что для завершения работы каждой службе выделяется 20 секунд, после чего она останавливается принудительно. Однако тесты показали, что это условие выполняется не всегда и служба принудительно останавливается до истечения этого промежутка времени.

Система безопасности служб


Любое действие над службами требует наличия соответствующих прав у приложения. Все приложения обладают правами на соединение с SCM, перечисление служб и проверку заблокированности БД службы. Регистрировать в сиситеме новую службу или блокировать БД службы могут только приложения, обладающие административными правами.

Каждая служба имеет дескриптор безопасности, описывающий какие пользователи имеют права на ту или иную операцию. По умолчанию:

  • Все пользователи имеют права SERVICE_QUERY_CONFIG, SERVICE_QUERY_STATUS, SERVICE_ENUMERATE_DEPENDENTS, SERVICE_INTERROGATE и SERVICE_USER_DEFINED_CONTROL;
  • Пользователи, входящие в группу Power Users и учетная запись LocalSystem дополнительно имеют права SERVICE_START, SERVICE_PAUSE_CONTINUE и SERVICE_STOP;
  • Пользователи, входящие в группы Administrators и System Operators имеют право SERVICE_ALL_ACCESS.

Службы и интерактивность


По умолчанию интерактивные службы могут выполняться только в контексте безопасности LocalSystem. Это связано с особенностями вывода на экран монитора в Windows NT, где существует, например, такой объект как “Desktop”, для работы с которым нужно иметь соответствующие права доступа, которых может не оказаться у произвольной учетной записи, отличной от LocalSystem. Несмотря на то, что в подавляющем большинстве случаев это ограничение несущественно однако иногда существует необходимость создать службу, которая выводила бы информацию на экран монитора и при этом выполнялась бы в контексте безопасности отличном от LocalSystem, например, серверная компонента приложения для запуска приложений на удаленном компьютере.

Фрагмент кода из Примера 1. иллюстрирует такую возможность.

В этом фрагменте в ответ на запрос, посланный клиентской частью приложения последством RPC, служба выводит текстовое сообщение на экран монитора.

Пример службы (ключевые фрагменты)


Рассмотрим на примере ключевые фрагменты приложения на языке С++, реализующего службу Windows NT. Для наглядности несущественные части кода опущены.

Функция main


В Примере 2. показан код функции main.

Функция ServiceMain


Особенностью кода, содержащегося в ServiceMain, является то, что часто невозможно заранее предсказать время выполнения той или иной операции, особенно, если учесть, что ее выполнение происходит в операционной системе с вытесняющей многозадачностью. Если операция продлится дольше указанного в параметре вызова SetServiceStatus интервала времени, служба не сможет во-время отправить следующее уведомление, в результате чего SCM остановит ее работу. Примерами потенциально «опасных» операций могут служить вызовы функций работы с сетью при больших таймаутах или единовременное чтение большого количества информации с медленного носителя. Кроме того, такой подход совершенно не применим при отладке службы, поскольку выполнение программы в отладчике сопровождается большими паузами, необходимыми разработчику.

Для преодоления этой проблемы все операции по взаимодействию с SCM следует выполнять в отдельном потоке, не зависящем от действий, происходящих на этапе инициализации.

В Примере 3. показан алгоритм корректного запуска службы, использующий вспомогательный поток.

Функция Handler


В Примере 4. показан код функции Handler и вспомогательных потоков. Для запросов “Stop” и “Shutdown” используется алгоритм корректного останова службы, аналогичный тому, который используется при старте службы, с той лишь разницей, что вместо параметра SERVICE_START_PENDING в SetserviceStatus передается параметр SERVICE_STOP_PENDING, а вместо SERVICE_RUNNING — SERVICE_STOPPED.

В идеале для запросов “Pause” и “Continue” тоже следует использовать этот подход. Любознательный читатель без труда сможет реализовать его, опираясь на данные примеры.

Заключение


В заключение хотелось бы отметить, что с переходом на Windows NT 2000 разработка служб не претерпела изменений. Службы по-прежнему остаются важной частью программного обеспечения на платформе Windows, что предоставляет разработчикам широкое поле деятельности.


Пример 1


// Функция, аналог MessageBox Win32 API
int ServerMessageBox(RPC_BINDING_HANDLE h, LPSTR lpszText,
 LPSTR lpszTitle, UINT fuStyle) 
{ 
 DWORD dwThreadId; 
 HWINSTA hwinstaSave; 
 HDESK hdeskSave; 
 HWINSTA hwinstaUser; 
 HDESK hdeskUser; 
 int result; 
 
 // Запоминаем текущие объекты “Window station” и “Desktop”.
 GetDesktopWindow(); 
 hwinstaSave = GetProcessWindowStation(); 
 dwThreadId = GetCurrentThreadId(); 
 hdeskSave = GetThreadDesktop(dwThreadId); 
 
 // Меняем контекст безопасности на тот, 
 // который есть у вызавшего клиента RPC
 // и получаем доступ к пользовательским 
 // объектам “Window station” и “Desktop”.
 RpcImpersonateClient(h); 
 hwinstaUser = OpenWindowStation(“WinSta0”, 
 FALSE, MAXIMUM_ALLOWED); 
 if (hwinstaUser == NULL) 
 { 
 RpcRevertToSelf(); 
 return 0; 
 } 
 SetProcessWindowStation(hwinstaUser); 
 hdeskUser = OpenDesktop(“Default”, 0, FALSE, MAXIMUM_ALLOWED);
 RpcRevertToSelf(); 
 if (hdeskUser == NULL) 
 { 
 SetProcessWindowStation(hwinstaSave); 
 CloseWindowStation(hwinstaUser); 
 return 0; 
 } 
 SetThreadDesktop(hdeskUser); 
 
 // Выводим обычное текстовое окно.
 result = MessageBox(NULL, lpszText, lpszTitle, fuStyle); 

 // Восстанавливаем сохраненные объекты 
 // “Window station” и “Desktop”.
 SetThreadDesktop(hdeskSave); 
 SetProcessWindowStation(hwinstaSave); 
 CloseDesktop(hdeskUser); 
 CloseWindowStation(hwinstaUser); 
 
 return result; 
}

Пример 2


void main()
{
 SERVICE_TABLE_ENTRY steTable[] = 
 {
 {SERVICENAME, ServiceMain},
 {NULL, NULL}
 };

 // Устанавливаем соединение с SCM. Внутри этой функции
 // происходит прием и диспетчеризация запросов.
 StartServiceCtrlDispatcher(steTable);
}

Пример 3


void WINAPI ServiceMain(DWORD dwArgc, LPSTR *psArgv)
{
 // Сразу регистрируем обработчик запросов.
 hSS = RegisterServiceCtrlHandler(SERVICENAME, ServiceHandler);

 sStatus.dwCheckPoint = 0;
 sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
 SERVICE_ACCEPT_PAUSE_CONTINUE; 
 sStatus.dwServiceSpecificExitCode = 0;
 sStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
 sStatus.dwWaitHint = 0;
 sStatus.dwWin32ExitCode = NOERROR;
 
 // Для инициализации службы вызывается функция InitService();
 // Для того, чтобы в процессе инициализации система не
 // выгрузила службу, запускается поток, который раз в 
 // секунду сообщает, что служба в процессе инициализации. 
 // Для синхронизации потока создаётся событие.
 // После этого запускается рабочий поток, для 
 // синхронизации которого также
 // создаётся событие.

 hSendStartPending = CreateEvent(NULL, TRUE, FALSE, NULL);

 HANDLE hSendStartThread;
 DWORD dwThreadId;
 
 hSendStartThread = CreateThread(NULL, 0, SendStartPending,
 NULL, 0, &dwThreadId);
 
 //Здесь производится вся инициализация службы.
 InitService();

 SetEvent(hSendStartPending);

 if(
 WaitForSingleObject(hSendStartThread, 2000) 
 != WAIT_OBJECT_0) 
 {
 TerminateThread(hSendStartThread, 0);
 }

 CloseHandle(hSendStartPending);
 CloseHandle(hSendStartThread);

 hWork = CreateEvent(NULL, TRUE, FALSE, NULL);

 hServiceThread = CreateThread(NULL, 0, ServiceFunc, 
 0, 0, &dwThreadId);

 sStatus.dwCurrentState = SERVICE_RUNNING;

 SetServiceStatus(hSS, &sStatus);
}

// Функция потока, каждую секунду посылающая уведомления SCM
// о том, что процесс инициализации идёт. Работа функции
// завершается, когда устанавливается 
// событие hSendStartPending.

DWORD WINAPI SendStartPending(LPVOID)
{
 sStatus.dwCheckPoint = 0;
 sStatus.dwCurrentState = SERVICE_START_PENDING;
 sStatus.dwWaitHint = 2000;

 // “Засыпаем” на 1 секунду. Если через 1 секунду 
 // событие hSendStartPending не перешло
 // в сигнальное состояние (инициализация службы не 
 // закончилась), посылаем очередное уведомление, 
 // установив максимальный интервал времени
 // в 2 секунды, для того, чтобы был запас времени до
 // следующего уведомления. 
 while (true)
 { 
 SetServiceStatus(hSS, &sStatus);
 sStatus.dwCheckPoint++; 
 if(WaitForSingleObject(hSendStartPending, 
 1000)!=WAIT_TIMEOUT)
 break;
 }

 sStatus.dwCheckPoint = 0;
 return 0;
}

// Функция, инициализирующая службу. Чтение данных,
// распределение памяти и т.п.
void InitService()
{
 ...
}

// Функция, содержащая «полезный» код службы.
DWORD WINAPI ServiceFunc(LPVOID)
{
 while (true)
 {
 if (!bPause)
 {
 // Здесь содержится код, который как правило 
 // выполняет какие-либо циклические операции...
 }

 if (WaitForSingleObject(hWork, 1000)!=WAIT_TIMEOUT)
 break;
 }

 return 0;
}

Пример 4


// Обработчик запросов от SCM
void WINAPI ServiceHandler(DWORD dwCode)
{
 switch (dwCode)
 {
 case SERVICE_CONTROL_STOP:
 case SERVICE_CONTROL_SHUTDOWN:
 ReportStatusToSCMgr(SERVICE_STOP_PENDING, 
 NO_ERROR, 0, 1000);
 hSendStopPending = CreateEvent(NULL, TRUE, FALSE, NULL);
 hSendStopThread = CreateThread(NULL, 0, 
 SendStopPending, NULL, 0, & dwThreadId);
 SetEvent(hWork);
 if (WaitForSingleObject(hServiceThread,
 1000) != WAIT_OBJECT_0)
 {
 TerminateThread(hServiceThread, 0);
 }
 SetEvent(hSendStopPending);
 CloseHandle(hServiceThread);
 CloseHandle(hWork);
 if(WaitForSingleObject(hSendStopThread,
 2000) != WAIT_OBJECT_0) 
 {
 TerminateThread(hSendStopThread, 0);
 }
 CloseHandle(hSendStopPending);

 sStatus.dwCurrentState = SERVICE_STOPPED;
 SetServiceStatus(hSS, &sStatus);
 break;
 case SERVICE_CONTROL_PAUSE:
 bPause = true;
 sStatus.dwCurrentState = SERVICE_PAUSED;
 SetServiceStatus(hSS, &sStatus);
 break;
 case SERVICE_CONTROL_CONTINUE:
 bPause = true;
 sStatus.dwCurrentState = SERVICE_RUNNING;
 SetServiceStatus(hSS, &sStatus);
 break;
 case SERVICE_CONTROL_INTERROGATE:
 SetServiceStatus(hSS, &sStatus);
 break;
 default:
 SetServiceStatus(hSS, &sStatus); 
 break;
 }
}


// Функция потока, аналогичная SendStartPending 
// для останова службы.
DWORD WINAPI SendStopPending(LPVOID)
{
 sStatus.dwCheckPoint = 0;
 sStatus.dwCurrentState = SERVICE_STOP_PENDING;
 sStatus.dwWaitHint = 2000;

 while (true)
 { 
 SetServiceStatus(hSS, &sStatus);
 sStatus.dwCheckPoint++; 
 if(WaitForSingleObject(hSendStopPending, 
 1000)!=WAIT_TIMEOUT)
 break;
 }

 sStatus.dwCheckPoint = 0;
 return 0;
}




Комментарии

 Ваш комментарий к данному материалу будет интересен нам и нашим читателям!



Последние статьи: Операционные системы / Windows /

Спасение WINDOWS XP
27-05-2010   

Windows крайне ненадежная операционная система. Сколько бы ни говорили, что XP не такой все это неправда. Он летит просто чуть меньше своих собратьев. Ну, еще, правда он имеет некоторые функции восстановления . Как раз о них я и расскажу в этой статье... подробнее

Кол. просмотров: общее - 3757 сегодня - 2

Реестр WINDOWS XP
27-05-2010   

Реестр. Огромная база данных где хранятся настройки нашей ос. Чего тут только нет! Любой программист создающий достаточно серьезную программу обязательно работает с реестром... подробнее

Кол. просмотров: общее - 3693 сегодня - 0

Командная строка Windows XP + .bat
27-05-2010   

Многие пользователи Windows просто игнорируют командную строку за ненадобностью, но для администратирования, написания программ - командный язык может оказаться весьма полезным... подробнее

Кол. просмотров: общее - 3804 сегодня - 0

Общие сведения о драйверах устройств в системе Windows
17-05-2010   

Естественно, каждая операционная система имеет собственную архитектуру и свои особенности функционирования драйверов. Но практически во всех современных ОС можно выделить следующие особенности, характерные для работы подсистемы ввода-вывода... подробнее

Кол. просмотров: общее - 3416 сегодня - 1

Система ввода-вывода в Windows
17-05-2010   

На данный момент наиболее распространены два семейства ОС Windows: Windows NT, куда относятся Windows NT, 2000, XP, и Windows 9x (Win 95, 98, ME). При этом отмечается тенденция к отмиранию ветки 9х, хотя такие системы будут встречаться еще достаточно долго... подробнее

Кол. просмотров: общее - 3683 сегодня - 0



  WWW.COMPROG.RU - 2009-2012 | Designed and Powered by Zaipov Renat | Projects