Вопрос 9. Службы в ОС Windows : назначение, основные принципы функционирования, отличие от драйверов. Порядок установки службы в ОС Windows .


Добавил:DMT
Дата создания:30 декабря 2007, 18:59
Дата обновления:10 января 2008, 0:06
Просмотров:12085 последний сейчас, 19:42
Комментариев: 0
Вопрос 9. Службы в ОС Windows : назначение, основные принципы функционирования, отличие от драйверов. Порядок установки службы в ОС Windows .

Службы - это программы, работающие вне контекста пользователя. Службы продолжают работать даже тогда, когда один пользователь отключается от системы и к ней подключается другой пользователь. Это единственная категория программ, которые способны работать в таком режиме. С точки зрения программиста, службы - это программы (чаще всего консольные), обладающие средствами связи с диспетчером служб SCM ( Service Control Manager ). Диспетчер SCM — это внутренний механизм Windows , осуществляющий управление службами. В Windows NT 4 пользователь взаимодействует с SCM при помощи апплета Services Control Panel . В Windows 2000 ту же функцию выполняет один из модулей консоли ММС.

Архитектура системных служб Windows NT подразумевает три вида. Ee ядром является менеджер системных служб ( Service Control Manager или SCM). SCM запускается при загрузке системы и работает, пока компьютер не будет выключен. Менеджер системных служб взаимодействует с одной стороны с управляющими программами , а с другой - с системными службами . Основными задачами менеджера являются:

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

Windows NT определяет два вида системных служб. Один из них - это так называемые службы режима ядра ( kernel-mode services ) - драйверы устройств. Другой вид - службы Win32 , обычные Win32-процессы, использующие специальный набор функций для взаимодействия с менеджером системных служб.

Служба обладает функцией main . Одна программа может обеспечивать работу одной службы, а может включать в себя сразу несколько служб. Функция main заполняет массив, описывающий все поддерживаемые программой службы. Массив передается вызову StartServiceCtrlDispatcher . Для каждой службы, входящей в состав исполняемого файла, этот массив содержит текстовый идентификатор и точку входа. Точка входа службы - функция типа void , принимающая обычные аргументы argc и argv . Но в отличие от аргументов функции main , аргументы точки входа службы не связаны с командной строкой. Они заполняются диспетчером SCM .

В процессе обращения SCM к функции, которая является точкой входа службы, важную роль играет структура SERVICE _ STATUS . Функция, являющаяся точкой входа, использует вызов RegisterServiceCtrlHandler для того, чтобы зарегистрировать обработчик, предназначенный для взаимодействия SCM со службой. Помимо этого подпрограмма инициализации службы обращается к вызову SetServiceStatus для того, чтобы проинформировать SCM о текущем состоянии службы (служба может находиться в одном из состояний: инициализация, функционирование или остановка). Если инициализация требует более чем несколько секунд, рекомендуется выполнить ее в отдельном программном потоке. Создание отдельного программного потока потребуется также в случае, если вы намерены создать службу, которая будет работать исключительно в фоновом режиме.

Когда диспетчер SCM желает тем или иным образом обратиться к службе, он обращается к обработчику, зарегистрированному при помощи вызова RegisterServiceCtrlHandler . Чаще всего это происходит тогда, когда диспетчер SCM желает завершить работу службы, приостановить ее исполнение или продолжить ее работу после паузы. Диспетчер SCM обращается к обработчику также в случае, если требуется определить текущее состояние службы. Помимо этого служба может определять другие специальные виды запросов, которые можно адресовать службе при помощи вызова Control Service (этот вызов является частью диспетчера SCM ). Для того чтобы направить службе запрос, необходимо обладать необходимыми полномочиями.

 

Установка служб

Чтобы установить службу, необходимо добавить в реестр несколько записей. Чаще всего эта процедура выполняется при помощи SCM . Для этого необходимо открыть SCM (при помощи вызова OpenSCManager ), а затем обратиться к вызову CreateService . В качестве аргументов этот вызов принимает имя файла, в котором содержится служба, а также другую необходимую информацию. Затем функция StartService , посылает запрос на запуск службы менеджеру системных служб. SCM определяет, что процесс, соответствующий службе, еще не запущен, поэтому он вызывает функцию CreateProcessAsUser , передавая ей командную строку, указанную в конфигурации службы. Далее стартует процесс службы. Управление передается функции WinMain (или просто main ) процесса службы. Произведя необходимую инициализацию, WinMain вызывает функцию StartServiceCtrlDispatcher , которая реализует цикл приема и обработки команд SCM. StartServiceCtrlDispatcher устанавливает соединение со SCM и сразу же получает от него первую команду, а именно команду на запуск службы. Кроме того, менеджер системных служб сообщает приложению, запустившему службу, что служба начала выполняться. Именно в этот момент StartService возвращает управление прикладной программе. Получив команду на запуск службы, StartServiceCtrlDispatcher создает новый поток вызовом CreateThread , в котором вызывает главную функцию службы, обычно имеющую название ServiceMain . Непосредственно перед созданием потока StartServiceCtrlDispatcher вызывает SetServiceStatus чтобы установить текущее состояние службы в SERVICE_START_PENDING, указывая при этом dwCheckPoint как 0 и dwWaitHint как 2000. Функция службы получает управление и одним из первых действий вызывает функцию RegisterServiceCtrlHandler , чтобы зарегистрировать функцию-обработчик команд для этой службы. Служба выполняет инициализацию, например, после чего вызывает SetServiceStatus , указывая состояние SERVICE_RUNNING. С этого момента служба считается стартовавшей.

Основное отличие драйвера от службы заключается в том, что драйвер может работать в 0 кольце и в пользовательском режиме, а служба работает только в пользовательском режиме. Установка службы осуществляется через SCM менеджер, а драйвер через . inf файл, однако драйвер может быть установлен через SCM менеджер, но не все типы драйверов могут установлены через SCM менеджер.

 

Код службы, которая издает звуковой сигнал в начале каждого часа (период времени можно изменить из командной строки):

Код на C++
  1. #include <windows.h>
  2. #include <iostream.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #include <process.h>
  6.  
  7. SERVICE_STATUS Service1Status;
  8. SERVICE_STATUS_HANDLE Service1StatusHandle;
  9.  
  10. int modtime=3600; // звуковой сигнал каждый час
  11. HANDLE thread;
  12. BOOL manual=FALSE;
  13.  
  14. void ding(void *)
  15. {
  16. time_t timeval;
  17. while (Service1Status.dwCurrentState!=SERVICE_STOPPED)
  18. {
  19. if (Service1Status.dwCurrentState!=SERVICE_PAUSED)
  20. {
  21. time(&timeval);
  22. if (!(timeval%modtime)|| manual)
  23. PlaySound( "DING", GetModuleHandle( NULL), SND_RESOURCE|SND_SYNC|SND_NOSTOP);
  24. manual=FALSE;
  25. }
  26. Sleep(500);
  27. }
  28. }
  29.  
  30. VOID __stdcall CtrlHandler (DWORD Opcode)
  31. {
  32. DWORD status;
  33. switch(Opcode)
  34. {
  35. case SERVICE_CONTROL_PAUSE:
  36. // если пауза
  37. Service1Status.dwCurrentState = SERVICE_PAUSED;
  38. break;
  39. case SERVICE_CONTROL_CONTINUE:
  40. // если продолжение работы
  41. Service1Status.dwCurrentState = SERVICE_RUNNING;
  42. // блокировать звуковой сигнал
  43. manual=TRUE;
  44. break;
  45. case SERVICE_CONTROL_STOP:
  46. //остановка службы
  47. Service1Status.dwWin32ExitCode = 0;
  48. Service1Status.dwCurrentState = SERVICE_STOPPED;
  49. Service1Status.dwCheckPoint = 0;
  50. Service1Status.dwWaitHint = 0;
  51. if (!SetServiceStatus (Service1StatusHandle,&Service1Status))
  52. {
  53. status = GetLastError();
  54. }
  55. return;
  56. case SERVICE_CONTROL_INTERROGATE:
  57. //Запрашивает текущее состояние службы
  58. break;
  59. default:
  60. // обработка неизвестного кода
  61. // ничего не делать
  62. ;
  63. }
  64. // текущее состояние
  65. if (!SetServiceStatus (Service1StatusHandle, &Service1Status))
  66. {
  67. status = GetLastError();
  68. }
  69. return;
  70. }
  71.  
  72. void __stdcall Service1Start (DWORD argc, LPTSTR *argv)
  73. {
  74. DWORD status;
  75. DWORD specificError;
  76. if (argc>1) modtime=atoi(argv[1]);
  77. if (!modtime) modtime=1;
  78. Service1Status.dwServiceType = SERVICE_WIN32; // тип службы
  79. Service1Status.dwCurrentState = SERVICE_START_PENDING; // состояние службы
  80. //набор флагов, который показывает, какие команды управления обрабатываются службой
  81. Service1Status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
  82. Service1Status.dwWin32ExitCode = 0;
  83. Service1Status.dwServiceSpecificExitCode = 0;
  84. Service1Status.dwCheckPoint = 0;
  85. Service1Status.dwWaitHint = 0;
  86. Service1StatusHandle = RegisterServiceCtrlHandler(TEXT("Servicel"), CtrlHandler);
  87. //регистрация функции-обработчика команд службы
  88. if (Service1StatusHandle == (SERVICE_STATUS_HANDLE)0)
  89. {
  90. // ошибка!
  91. return;
  92. }
  93. // Код инициализации переходит сюда
  94. status=NO_ERROR;
  95. // Условие ошибочности
  96. if (status != NO_ERROR)
  97. {
  98. Service1Status.dwCurrentState = SERVICE_STOPPED;
  99. Service1Status.dwCheckPoint = 0;
  100. Service1Status.dwWaitHint =0;
  101. Service1Status.dwWin32ExitCode = status;
  102. Service1Status.dwServiceSpecificExitCode = specificError;
  103. SetServiceStatus (Service1StatusHandle, &Service1Status);
  104. return;
  105. } // Инициализация завершена - доложить о готовности.
  106. Service1Status.dwCurrentState = SERVICE_RUNNING;
  107. Service1Status.dwCheckPoint = 0;
  108. Service1Status.dwWaitHint = 0;
  109. if (!SetServiceStatus (Service1StatusHandle, &Service1Status))
  110. {
  111. status = GetLastError();
  112. // ошибка!
  113. }
  114. // Здесь служба выполняет свою работу.
  115. thread=(HANDLE)_beginthread(ding,0,NULL);
  116. return;
  117. }
  118.  
  119. void main(int argc, char *argv[])
  120. {
  121. SERVICE_TABLE_ENTRY DispatchTable[] =
  122. {
  123. {
  124. TEXT("Servicel"), //текстовый идентификатор службы
  125. Service1Start //точка входа службы
  126. },
  127. {
  128. NULL,NULL
  129. }
  130. };
  131.  
  132. if (argc>1 && !stricmp(argv[1],"delete"))
  133. {
  134. //соединяемся с менеджером системных служб
  135. SC_HANDLE scm=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
  136. if (!scm)
  137. {
  138. cout<<"He могу открыть SCM\n";
  139. exit(1);
  140. }
  141. //получаем хэндл службы
  142. SC_HANDLE svc=OpenService(scm,"service1",DELETE);
  143. if (!svc)
  144. {
  145. cout<<"He могу открыть службу\n";
  146. exit(2);
  147. }
  148. //удаляем службу
  149. if (!DeleteService(svc))
  150. {
  151. cout<<"He могу удалить службу\n";
  152. exit(3);
  153. }
  154. cout<<"Служба удалена\n";
  155. CloseServiceHandle(svc);
  156. CloseServiceHandle(scm);
  157. exit(0);
  158. }
  159.  
  160. //регистрация службы
  161. if (argc>1 && !stricmp(argv[1], "setup"))
  162. {
  163. char pname[1024];
  164. pname[0]='\"';
  165. //получаем имя файла, которому принадлежит данный процесс
  166. GetModuleFileName(NULL,pname+1,1023);
  167. strcat(pname,"\"");
  168. //соединяемся с менеджером системных служб
  169. //тип доступа: Создание новой системной
  170. //службы с помошью CreateService
  171. SC_HANDLE scm=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE),svc;
  172. if (!scm)
  173. {
  174. cout<<"Can't open SCM\n";
  175. exit(1);
  176. }
  177. //создаем новую службу
  178. if (!(svc=CreateService(scm, //handle SCM
  179. "Service1", //имя службы
  180. "Service1", //название службы
  181. SERVICE_ALL_ACCESS, //доступ к объекту службы
  182. SERVICE_WIN32_OWN_PROCESS,
  183. //тип службы - Служба Win32, выполняющаяся в
  184. //отдельном процессе
  185. SERVICE_DEMAND_START,
  186. //способ запуска службы: запускается, при вызове
  187. //функции StartService
  188. SERVICE_ERROR_NORMAL,
  189. //реакция на ошибки при загрузке: Программа загрузки
  190. //заносит запись в системный журнал и загрузка продолжается.
  191. //Кроме того, на экран выводится сообщение, что службу не
  192. //удалось запустить
  193. pname, //исполняемый файл
  194. NULL, //группа загрузки
  195. NULL, //тег (NULL для служб Win32)
  196. NULL, //зависимости
  197. NULL,
  198. //учетная запись службы (NULL соответствует учетной записи LocalSystem)
  199. NULL
  200. //пароль учетной записи
  201. )))
  202. {
  203. cout<<"Ошибка регистрации!\n";
  204. exit(2);
  205. }
  206. cout<<"Успешно зарегистрирована служба "<<pname<<"\n";
  207. CloseServiceHandle(svc);
  208. CloseServiceHandle(scm);
  209. exit(0);
  210. }
  211. //цикл приема и обработки команд SCM
  212. if (!StartServiceCtrlDispatcher( DispatchTable))
  213. {
  214. // error
  215. }
  216. }
При использовании обязательна ссылка на http://DMTSoft.ru
up