Есть задача правильно завершить долгозавершающуюся рабочую службу. Служба моя.
Написал тестовую службу для отработки завершения, которая завершается 5 минут. Через 2 минуты после отправки команды stop приложение Cлужбы выдает ошибку, Служба не ответила на запрос своевременно.
Тестовая служба отправляет сообщение SERVICE_STOP_PENDING раз в секунду при завершении. Используется dwCheckPoint, dwWaitHint.
Windows 10.
Возможно ли без ошибок завершить тестовую службу в приложении Службы?
Часть ServiceMain.
while (1)
{
WaitForSingleObject(ghSvcStopEvent, INFINITE);
for (int i = 0; i < 300; i++)
{
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 2000);
Sleep(1000);
}
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
Обработка сообщений от SCM.
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
{
switch(dwCtrl)
{
case SERVICE_CONTROL_STOP:
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
// Signal the service to stop.
SetEvent(ghSvcStopEvent);
return;
case SERVICE_CONTROL_INTERROGATE:
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
break;
default:
break;
}
}
Сообщение о статусе SCMу.
VOID ReportSvcStatus( DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
gSvcStatus.dwCurrentState = dwCurrentState;
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
gSvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING)
gSvcStatus.dwControlsAccepted = 0;
else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
if ( (dwCurrentState == SERVICE_RUNNING) ||
(dwCurrentState == SERVICE_STOPPED) )
gSvcStatus.dwCheckPoint = 0;
else gSvcStatus.dwCheckPoint = dwCheckPoint++;
// Report the status of the service to the SCM.
SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
}
ЕМ>Если эта служба Ваша, то единственно правильным будет сделать ее так, чтобы она завершалась максимум за 10-20 секунд.
ЕМ>Если нет, то после истечения таймаута прибивать через TerminateProcess.
Рабочая служба моя. Она может завершиться самостоятельно, просто делает это за 40 секунд. Наверно, может и дольше завершаться.
Re[3]: Правильная остановка долгозавершающейся службы
Здравствуйте, nekoriu, Вы писали:
ЕМ>>Если эта служба Ваша, то единственно правильным будет сделать ее так, чтобы она завершалась максимум за 10-20 секунд.
ЕМ>>Если нет, то после истечения таймаута прибивать через TerminateProcess.
N>Рабочая служба моя. Она может завершиться самостоятельно, просто делает это за 40 секунд. Наверно, может и дольше завершаться.
А если не секрет, в чем причина такой могучей задержки? Что она — служба — делает-то в это время да так долго?
C>А если не секрет, в чем причина такой могучей задержки? Что она — служба — делает-то в это время да так долго?
Служба моя в том смысле, что у меня есть ее код. Писал ее не я. Мне нужно подправить ее завершение. Служба работает с несколькими БД, имеет несколько плагинов, прослушивает сокет и выполняет команды. Завершение 40 секунд у Debug конфигурации. Надо будет попробовать с конфигурацией Release.
Re[5]: Правильная остановка долгозавершающейся службы
Здравствуйте, nekoriu, Вы писали:
C>>А если не секрет, в чем причина такой могучей задержки? Что она — служба — делает-то в это время да так долго? N>Служба моя в том смысле, что у меня есть ее код. Писал ее не я. Мне нужно подправить ее завершение. Служба работает с несколькими БД, имеет несколько плагинов, прослушивает сокет и выполняет команды. Завершение 40 секунд у Debug конфигурации. Надо будет попробовать с конфигурацией Release.
По моему тут ошибка в архитектуре, в общей схеме организации работы службы, а не техническая проблема…
А работать с БД, и слушать сокет нельзя что ли в соседнем потоке? Когда основной поток службы получит сигнал о завершении, то пусть взведет какой-нить примитив синхронизации (mutex, event и.т.п). Ну, а соответственно фоновые потоки (работа с БД, сокет) пусть периодически проверяют этот примитив синхронизации. И коль примитив взведен, то сразу на выход…
C>По моему тут ошибка в архитектуре, в общей схеме организации работы службы, а не техническая проблема…
C>А работать с БД, и слушать сокет нельзя что ли в соседнем потоке? Когда основной поток службы получит сигнал о завершении, то пусть взведет какой-нить примитив синхронизации (mutex, event и.т.п). Ну, а соответственно фоновые потоки (работа с БД, сокет) пусть периодически проверяют этот примитив синхронизации. И коль примитив взведен, то сразу на выход…
C>Не? Так не подойдет разве?
Архитектуру менять — большие затраты. Соседние потоки есть и они выполняют свои функции. Архитектура такая, как вы описали. Нужно ждать завершения фоновых потоков, сразу на выход они не могут.
Re[7]: Правильная остановка долгозавершающейся службы
Здравствуйте, nekoriu, Вы писали:
C>>По моему тут ошибка в архитектуре, в общей схеме организации работы службы, а не техническая проблема…
C>>А работать с БД, и слушать сокет нельзя что ли в соседнем потоке? Когда основной поток службы получит сигнал о завершении, то пусть взведет какой-нить примитив синхронизации (mutex, event и.т.п). Ну, а соответственно фоновые потоки (работа с БД, сокет) пусть периодически проверяют этот примитив синхронизации. И коль примитив взведен, то сразу на выход…
C>>Не? Так не подойдет разве? N>Архитектуру менять — большие затраты.
А зачем ее менять, если архитектура, судя во Вашей цитате, и так «Архитектура такая, как вы описали»!?!
Значит всё и так боле-менее хорошо…
N>Соседние потоки есть и они выполняют свои функции. Архитектура такая, как вы описали. N>Нужно ждать завершения фоновых потоков, сразу на выход они не могут.
Это кагбэ понятно, что нужно ждать… А почему фоновые потоки так долго выходят?
PS: полагаю, ересь, сейчас напишу… Понимаю, что все равно это из разряда изменения архитектуры.
Но всё ж… В стиле мозгового штурма, когда даже идиотские идеи вносятся в список, и откладываются на время для дальнейшего анализа.
А что если код этих самых "тяжелых" задач (БД, слушки сокетов) вынести вообще в отдельный процесс (не службу)?
И схема работы примерно такая: 1) Стартует служба
2) Запускает все эти "тяжелые" задачи (БД и прочия) в отдельном обычном процессе..
3) Синхронится с этими отдельными процессами как-то там через примитив (какой-нить именованный эвент что ли).
4) Служба получает сигнал "остановиться".
5) Взводит какой-нить примитив для тяжелых задач на предмет "давай заканчивай" и быстренько выходит…
6) Эти "тяжелые" задачи посматривают на примитив "давай заканчивай" и по мере возможности постепенно завершаются.
В этом случае служба суть всего лишь управление, и она может быстро завершиться. А "тяжелые" задачи — не службы. Они могут завершаться сколько им надо.
Если системе невтерпёж, и она прибьет такой фоновый процесс с тяжелой задачей, то и фиг с ним. Это те же проблемы, что и с TerminateThread, только в профиль. Но есть вариант, что фоновый процесс с тяжелой задачей завершиться нормально, пусть хоть и большЕй задержкой…
Как-то так!?!
Правда, синхронизацию там более чем аккуратно придется писать. Плюс (возможно) понадобится еще и передача данных между процессами (pipes, shared memory и иже с ними). Та еще развлекуха, если вдруг что пойдет не так…
Здравствуйте, nekoriu, Вы писали:
N>Написал тестовую службу для отработки завершения, которая завершается 5 минут. Через 2 минуты после отправки команды stop приложение Cлужбы выдает ошибку, Служба не ответила на запрос своевременно.
N>Возможно ли без ошибок завершить тестовую службу в приложении Службы?
With a simple code change, a preshutdown service can instruct Windows to wait more than the default 3 minutes for the service to end in the preshutdown phase.
You must add code to call the ChangeServiceConfig2 function with the SERVICE_CONFIG_PRESHUTDOWN_INFO level, and the timeout value in the SERVICE_PRESHUTDOWN_INFO structure set to an appropriate value.
Here is what the addition looks like in C++ (error checking omitted for brevity):
The new code is best called once when you install/setup your service, but it can be invoked during normal operation as well. Whichever works better in your situation.
Re: Правильная остановка долгозавершающейся службы
Здравствуйте, nekoriu, Вы писали:
N>Есть задача правильно завершить долгозавершающуюся рабочую службу. Служба моя.
N>Написал тестовую службу для отработки завершения, которая завершается 5 минут. Через 2 минуты после отправки команды stop приложение Cлужбы выдает ошибку, Служба не ответила на запрос своевременно.
Несмотря на застарелый тред (больше года утекло) попытаюсь ответить. Возможно, кому-то будет полезно.
Я написал свой тестовый сервис, который использует механизм чекпоинтов. 5 минут стопится, отрабатывает без проблем, Win7 Pro. Здесь нужно добавить, что управлял я сервисом при помощи утилиты sc.exe, поскольку давно привык к этому в разработке. Запомним этот момент.
Затем посетила меня мысль пойти в оснастку сервисов и поуправлять при помощи неё. Вуаля, через 2 минуты получаю "Служба не ответила на запрос..." Опять же нужно добавить, что при остановке службы я любовался прогресс-баром оснастки. Окей, кто-то здесь лишний, ведь я точно знаю, что сервис рабочий.
(между тем, пришлось ребутнуть винду, т.к. сервис не хотел уходить из оснастки)
После перезагрузки снова запускаю и останавливаю мой сервис из оснастки, но теперь сразу отказываюсь от диалога с прогресс-баром. Контролирую остановку, как и раньше, командами sc query SvcLongStop. (SvcLongStop — название сервиса) Через 5 минут сервис штатно останавливается, но оснастка об этом не знает. Жму "Обновить" и вижу что сервис отражается теперь и в оснастке, как остановленный.
Итого в сухом остатке:
механизм чекпоинтов можно и нужно использовать для долгих переходных операций; сам по себе долгий переход не несет в себе ничего криминального и не надо пенять на архитектуру
для контроля за сервисами желательно использовать не- или слабо-интерактивные утилиты, либо написать свою, либо воспользоваться чужой, но проверенно надежной
майкрософту в целом верить можно, но иногда они дико косячат, и с этим ничего поделать нельзя, просто надо научится с этим жить
Нелишне добавить, что документация требует указывать dwWaitHint:
The estimated time required for a pending start, stop, pause, or continue operation, in milliseconds.
Какбы нелогично указывать его равным нулю. Но я потратил время на проверки (Win7 Pro) и не увидел разницы в поведении между 0 ms, точным значением таймаута между чекпоинтами 1000 ms и целиком пятиминутным значением. Сервисменеджеру просто по барабану. Ну и ладно. Смотри выше про майкрософт.
ИВ> для контроля за сервисами желательно использовать не- или слабо-интерактивные утилиты, либо написать свою, либо воспользоваться чужой, но проверенно надежной ИВ>
Или не выпендриваться, и не допускать задержек, не имеющих убедительного обоснования.
Re[3]: Правильная остановка долгозавершающейся службы
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Здравствуйте, Игорь Вартанов, Вы писали:
ИВ>>
ИВ>> для контроля за сервисами желательно использовать не- или слабо-интерактивные утилиты, либо написать свою, либо воспользоваться чужой, но проверенно надежной ИВ>>
ЕМ>Или не выпендриваться, и не допускать задержек, не имеющих убедительного обоснования.
Да, да. Обязательно надо убедительно убедить и не а бы кого, а именно Музыченко!