Самый чистый способ остановить процесс на Win32?

При реализации прикладного сервера и его клиентских библиотек на C ++ у меня возникают проблемы с поиском чистого и надежного способа остановки клиентских процессов при завершении работы сервера в Windows.

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

  • the solution should work in the following cases:
    • clients may each feature either a console or a gui.
    • user may be unprivileged.
    • clients may be or become unresponsive (infinite loop, deadlock).
    • клиенты могут быть или не быть дочерними по отношению к серверу (прямым или косвенным).
  • если это не предотвращено из-за дефекта на стороне клиента, клиентам должна быть предоставлена ​​возможность полностью завершить работу (освободить свои ресурсы, синхронизировать некоторые данные с диском ...) и некоторое разумное время для этого.
  • все коды возврата клиента должны быть доступны (если возможно) серверу во время процедуры выключения.
  • сервер будет ждать, пока не исчезнут все клиенты.

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

Я не сказал, что сервер также используется для запуска клиентов и в некоторых случаях других программ / сценариев, которые вообще не используют клиентскую библиотеку. Решение, которое не полагалось бы на изящную связь между сервером и клиентами, было бы лучше (если возможно).

Некоторое время назад я наткнулся на фрагмент C (я полагаю, в MSDN), который делал следующее:

  1. запустить поток через CreateRemoteThread в процессе завершения.
  2. этот поток напрямую вызвал ExitProcess.

К сожалению, теперь, когда я ищу его, я не могу его найти, и результаты поиска, похоже, предполагают, что этот трюк больше не работает в Vista. Есть ли мнение экспертов по этому поводу?


person bltxd    schedule 17.09.2008    source источник


Ответы (5)


Если вы используете поток, простое решение - использовать именованное системное событие, поток «засыпает» событием, ожидая его сигнала, управляющее приложение может сигнализировать о событии, когда оно хочет, чтобы клиентские приложения завершились.

Для приложения пользовательского интерфейса он (поток) может отправлять сообщение в главное окно, WM_ CLOSE или QUIT. Я забыл, что в консольном приложении он может выдать CTRL-C или, если основной код консоли зацикливается, он может проверить какое-то условие выхода. устанавливается по нитке.

В любом случае вместо того, чтобы обнаруживать, что клиентские приложения говорят им о завершении работы, используйте ОС, чтобы подать сигнал о том, что они должны выйти. Спящий поток практически не потребляет ресурсов ЦП при условии, что он использует WaitForSingleObject для сна.

person titanae    schedule 17.09.2008

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

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

person Asaf Bartov    schedule 17.09.2008

Если вы хотите использовать IPC-маршрут, сделайте нормальную связь между клиентом и сервером двунаправленной, чтобы сервер мог попросить клиентов выключиться. Или, если это не удастся, попросите клиентов провести опрос. Или, в крайнем случае, клиенты должны быть проинструктированы о завершении работы при отправке запроса на сервер. Вы можете позволить пользователю библиотеки зарегистрировать обратный вызов выхода, но лучший способ, который я знаю, - это просто вызвать «exit» в клиентской библиотеке, когда клиенту предлагается завершить работу. Если клиент застревает в коде выключения, сервер должен иметь возможность обойти это, игнорируя структуры данных и соединение этого клиента.

person Community    schedule 17.09.2008

Используйте PostMessage или именованное событие.

Re: PostMessage - приложения, отличные от GUI, а также потоки, отличные от потока GUI, могут иметь циклы сообщений, и это очень полезно для подобных вещей. (Фактически, COM использует циклы сообщений под капотом.) Я делал это раньше с помощью ATL, но немного устарел от этого.

Если вы хотите быть устойчивыми к злонамеренным атакам со стороны «плохих» процессов, включите закрытый ключ, совместно используемый клиентом / сервером, в качестве одного из параметров сообщения.

Подход с использованием именованных событий, вероятно, проще; используйте CreateEvent с именем, которое является секретом, совместно используемым клиентом / сервером, и попросите соответствующее приложение проверить статус события (например, WaitForSingleObject с таймаутом 0) в своем основном цикле, чтобы определить, следует ли завершить работу.

person Jason S    schedule 24.12.2008

Это очень общий вопрос, и в нем есть некоторые несоответствия.

Хотя это не 100% правило, большинство консольных приложений работают до завершения, тогда как приложения с графическим интерфейсом выполняются до тех пор, пока пользователь не завершит их (а службы работают до остановки через SCM). Следовательно, проще запросить закрытие графического интерфейса. Вы отправляете им эквивалент Alt-F4. Но для консольной программы вы должны отправить им эквивалент Ctrl-C и надеяться, что они справятся с этим. В обоих случаях вы просто ждете. Если процесс задерживается, вы затем сбиваете его (TerminateProcess) и молитесь, чтобы урон был ограничен. Но ваш жесткий диск может заполниться временными файлами.

У приложения с графическим интерфейсом вообще нет кодов выхода - куда бы они пошли? Консольный процесс, принудительно завершенный по определению, не завершается, поэтому у него нет кода выхода. Итак, в сценарии завершения работы сервера не ждите кодов выхода.

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

person MSalters    schedule 17.09.2008
comment
Вы абсолютно правы насчет кодов выхода завершенных процессов. Поскольку мы их устранили, мы знали, что они все равно не вышли из игры. Однако оставшаяся часть вашего ответа меня удивляет. Я подробно расскажу ниже. - person bltxd; 17.09.2008
comment
И графические, и консольные приложения являются процессами и поэтому имеют коды выхода. Имеют ли они смысл или нет - совсем другой вопрос. - person bltxd; 17.09.2008