Легенда:
новое сообщение
закрытая нитка
новое сообщение
в закрытой нитке
старое сообщение
|
- Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
- Новичкам также крайне полезно ознакомиться с данным документом.
[Win32] Не могу корректно остановить сервис NT. 23.05.04 23:43
Автор: void <Grebnev Valery> Статус: Elderman Отредактировано 23.05.04 23:44 Количество правок: 1
|
Подскажите, пожалуйста, как грамотно остановить сервис, обеспечить корректное завершение рабочего потока?
Предполагается, что сервис останавливается из mmc SCM. При завершении работы необходимо, чтобы рабочий поток освободил все используемые ресурсы.
В call-back хендлере, собственно, сделана робкая попытка только установить некий глобальный флаг g_bStopSniff = TRUE. Предполагалось (по наивности ), что рабочий поток сможет проанализировать g_bStopSniff ==TRUE ;)))Самое смешное, что некоторые гуру так и делают, блин ;))
Т.е хендлер:
DWORD WINAPI ServiceCtrlHandler( DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext )
{
DWORD rc = NO_ERROR;
switch (dwControl) {
case SERVICE_CONTROL_STOP:
g_bStopSniff = TRUE;
ss.dwWin32ExitCode = 0;
ss.dwCurrentState = SERVICE_STOPPED;
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
break;
case SERVICE_CONTROL_PAUSE:
<skip>
default:
rc = ERROR_CALL_NOT_IMPLEMENTED;
break;
}
SetServiceStatus( hSS, &ss);
return rc;
}
Рабочим потоком может быть как, как собственно void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv), так и вторичный поток с функцией потока SniffThread:
void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv)
{
<skip>
ss.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( hSS, &ss);
hThread = _beginthreadex( NULL, 0, &SniffThread, NULL, 0, &thID );
if (! hThread)
err(_T("Error creating sniffer thread."));
}
а код рабочего потока примитивен:
BOOL sniff( BOOL bDebug )
{
WSAData wsaData;
if (WSAStartup(0x0202,(WSADATA *) &wsaData ))
{
err( _T("Error WSAStartup"),WSAGetLastError());
return FALSE;
}
<skip … >
SOCKET snfSock = socket( AF_INET, SOCK_RAW, IPPROTO_IP );
if( bind(snfSock, (SOCKADDR *)&sa, sizeof(SOCKADDR)) )
{
err(_T("Error bind"),WSAGetLastError());
return FALSE;
}
<skip … >
FILE* fl = _tfopen( szPathname, _T("w"));
while( ! g_bStopSniff )
{
<skip … >
}
closesocket( snfSock );
WSACleanup();
fclose( fl );
return TRUE;
}
Понятно, что я хочу в этом примере. Если bStopSniff == TRUE, то я хочу остановить цикл while, прибить сокет snfSock и закрыть файл fl. Если там были выделены ещё какие-нить ресурсы, то хочу их освободить корректно.
В общем, что видится… Когда SCM вызывает хендлер ServiceCtrlHandler (SERVICE_CONTROL_STOP), то такое ощущение, что и ServiceMainProc и его вторичные потоки (если таковые были) – всё уже прибито SCM-ом ;( Поздно пить боржоми, чтобы чего-то там освободить ;((
Как быть, научите, плиз, недопрограммера ;)))
|
|
Остановить все потоки. установить свой статус в stopped... 24.05.04 00:18
Автор: Killer{R} <Dmitry> Статус: Elderman
|
> Подскажите, пожалуйста, как грамотно остановить сервис, > обеспечить корректное завершение рабочего потока? Остановить все потоки. Установить свой статус в STOPPED. Завершить выполнение ServiceMain.
> void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv) > { > <skip> > ss.dwCurrentState = SERVICE_RUNNING; > SetServiceStatus( hSS, &ss); > > hThread = _beginthreadex( NULL, 0, &SniffThread, > NULL, 0, &thID ); > if (! hThread) > err(_T("Error creating sniffer thread.")); > > } мм.. а дальше* ServiceMain должен завершаться только при завершении сервиса, а так висеть и к примеру ждать какого нить Mutex'а.
|
| |
Сорри, Вы наверное про это и постили - то, что в конце я там... 24.05.04 02:07
Автор: void <Grebnev Valery> Статус: Elderman
|
Сорри, Вы наверное про это и постили - то, что в конце я там со Sleep пропостил.
|
| |
1) Ты не понял. Рабочие потоки я могу создать, к примеру, в... 24.05.04 01:19
Автор: void <Grebnev Valery> Статус: Elderman
|
> Остановить все потоки. Установить свой статус в STOPPED.
1) Ты не понял. Рабочие потоки я могу создать, к примеру, в ServiceMain. Дескрипторы этих потоков и всего, что в них создаётся ( сокетов, файлов и т.д.) я могу сделать, конечно глобальными. Но! Особождать ресурсы из другого потока (например, из которого вызывается хендлер SCM) и оттуда прибивать рабочие потоки при помощи BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode );
- ИМХО bad desing.
Потоки сами должны каким-то образом получить оповещение о том, что юзверь кликнул в остнастке SCM "stop". Они сами должны освободить ресурсы, которые ранее запрашивали, а затем функция потока должна завершиться, вернув 0.
2) Когда я пытаюсь что-то установить в хендлере сервиса (например, свой флаг STOP) , то подозреваю, что поток ServiceMain уже прибит силой SCM. Другими словами, когда в ServiceCtrlHandler я получаю dwControl = SERVICE_CONTROL_STOP, то все потоки рождённые от ServiceMain выглядят совершенно мёртвыми. В этих потоках невозможно по описанному сценарию проанализировать некую глобальную переменную STOP, и затем корректно самостоятельно завершить работу.
> Завершить выполнение ServiceMain. > > void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR > *lpszArgv) > > { > > <skip> > > ss.dwCurrentState = SERVICE_RUNNING; > > SetServiceStatus( hSS, &ss); > > > > hThread = _beginthreadex( NULL, 0, &SniffThread, > > NULL, 0, &thID ); > > if (! hThread) > > err(_T("Error creating sniffer thread.")); > > > > } > мм.. а дальше* ServiceMain должен завершаться только при > завершении сервиса, а так висеть и к примеру ждать какого > нить Mutex'а.
Ошибаешься ;)))) В той части, что ServiceMain умеет, что-то ждать (хоть там критической секции, хоть Mutex-ов;))), если в ServiceCtrlHandler ты получил dwControl = SERVICE_CONTROL_STOP и в ответ установил статус сервиса ss.dwCurrentState = SERVICE_STOPPED, то ServiceMain и все дочерние её потоки (если таковые были), будутнещадно_прибиты SCM. ;))
Ниже, совершенно рабочий код с_while(1)_в конце. ServiceMain ни в жисть не подвиснет там. ;)) Этот поток просто будет убиенным SCM-омо при остановке сервиса.
void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv)
{
<skip>
ss.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( hSS, &ss);
while(1); // здесь поток не подвиснет. С ним расправится SCM ;)))
}
|
| | |
OOO !!! Вот я идиот. Но всё равно, подскажите 24.05.04 02:04
Автор: void <Grebnev Valery> Статус: Elderman Отредактировано 24.05.04 02:05 Количество правок: 1
|
Что называется, бывает хуже, но реже, блин....Добавил Sleep(1000). Естественно заработало ;)))
Т.е. та функция хендлера добавлена одной строчкой с :
DWORD WINAPI ServiceCtrlHandler( DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext )
{
DWORD rc = NO_ERROR;
switch (dwControl) {
case SERVICE_CONTROL_STOP:
g_bStopSniff = TRUE;
ss.dwWin32ExitCode = 0;
ss.dwCurrentState = SERVICE_STOPPED;
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
// HERE ;))) OOOOOOO !!!!
Sleep(1000);
break;
case SERVICE_CONTROL_PAUSE:
<skip>
default:
rc = ERROR_CALL_NOT_IMPLEMENTED;
break;
}
SetServiceStatus( hSS, &ss);
return rc;
}
Это работает. Но всё же. Как делают программисты более грамотно. Может перед установкой ss.dwCurrentState = SERVICE_STOPPED делать так:
case SERVICE_CONTROL_STOP:
bStopSniff = TRUE;
ss.dwCurrentState = SERVICE_STOP_PENDING;
ss.dwWaitHint = 1000;
SetServiceStatus( hSS, &ss);
Sleep(1000);
ss.dwWin32ExitCode = 0;
ss.dwCurrentState = SERVICE_STOPPED;
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
break;
Как думают программисты?
|
| | | | |
Да, внимательно не посмотрел вначале. Думал, что и так всё... 24.05.04 03:35
Автор: void <Grebnev Valery> Статус: Elderman
|
> Вот тут - > http://www.bugtraq.ru/cgi-bin/forum.mcgi?type=sb&b=2&m=1046 > 83 Да, внимательно не посмотрел вначале. Думал, что и так всё знаю. ;))) Чего смотреть ;))))
Конечно можно и так, при получении хендлером dwControl == SERVICE_CONTROL_STOP ответить только SetState(SERVICE_STOP_PENDING).
Далее дождаться эвента в рабочем потоке ServiceMain и там уже завершиться с установкой SetState(SERVICE_STOPPED).
Кстати, да. Так чисто получается ;)))
Ща мы тот код слямзим и отрехтуем в C ;)))
|
|
|