получить / проверить внутреннее состояние kernel32 для процесса win32 (для безопасного использования TerminateThread) - PullRequest
0 голосов
/ 20 апреля 2020

Я написал пул потоков с опцией завершения для потоков, доступных пользователю. Как описано в Документация по API terminateThread(),

Если целевой поток выполняет определенные вызовы kernel32, когда он завершается, состояние kernel32 для процесса потока может быть несовместимым.

Я мог бы сам проверить эту проблему: завершение потока в этом состоянии вызвало проблемы с выделением памяти (среди прочих), но исправление этого условия решило проблемы одновременно.

Вопросы

  1. Итак, я хочу проверять это внутреннее состояние после каждого использования terminateThread(). Если terminateThread() вызвало проблему для внутреннего состояния процесса в kernel32.dll, я хочу вызвать исключение - и завершить процесс после регистрации пользователя (если исправление внутреннего состояния все еще возможно).

    Это возможно? Может быть, найдя адрес соответствующей переменной состояния (или что-то в этом роде - сопоставив pdb-файл kernel32 или другим способом)? Ситуация плохая для меня - если я не могу решить ее, я должен либо опустить параметр завершения для пула потоков, либо просто оставить поток для себя. Будем благодарны за любые подсказки!

  2. Есть ли какая-либо другая функция win32, которая вызывает аналогичные проблемы?

  3. a. Безопасно ли оставлять поток для себя, когда он вызвал блокирующую функцию kernel32, которая точно никогда не вернется?

    b. Что произойдет, если функция win32 вернется и лямбда-функция будет уничтожена?

Почему я спрашиваю это? (Дополнительная информация)

У меня есть собственный поток потоков в моем проекте, где я вызываю некоторые win32 API, которые иногда могут блокироваться навсегда. Следовательно, я звоню им, используя тайм-аут. По истечении этого времени я вызываю terminateThread(), и мой пул потоков возвращает «неудавшееся состояние вызова».

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

Я также хотел бы повторно использовать этот пул потоков в других проектах, поэтому я должен сделать его безопасным.

Обновление: проблема исправлена.

Я обнаружил ошибку в своем приложении: фактически это был вызов terminateThread(), когда время ожидания в моем пуле потоков уже было мало (около 200 мс). Поток был прерван в момент, когда он не блокировался (то есть, если бы оставался более длительный период ожидания, он работал бы и возвращался правильно). Из трассировки стека ядра я обнаружил, что в режиме ядра мьютекс блокировался, пока поток был прерван, и пока поток выходил, другие потоки уже ждали этого мьютекса.

Сначала возникла проблема до go, увеличив минимальный тайм-аут до 1000 мс, но я не согласился с этим: мое решение состояло в том, чтобы создать лямбда в куче при достижении тайм-аута, оставить лямбду и поток для себя без завершения и добавить это к списку _ToTerminateThreads. Список завершается один раз в 10 минут (ждет 10 минут, копирует список, ждет еще одну минуту, а затем завершает потоки и удаляет лямбды).

Тем не менее, после тестирования и часов отладки я получал куча искажений. Наконец, я обнаружил следующее: потоки, которые я оставил для удаления, записали в память, которая использовалась пользовательской функцией (которая была передана в пул потоков), - и они были освобождены, потому что поток потоков вернулся. Это вызвало конечные проблемы, поэтому окончательным решением было увеличить время ожидания до безопасного уровня.

Я рекомендую всем, кому нужна такая функция, развернуть ее в дочернем процессе и вместо этого завершить этот процесс. использования темы.

Я держу этот вопрос открытым, потому что на основные 4 вопроса еще не ответили. Для моей проблемы мне больше не нужен их ответ, но они могут быть интересны для других членов stackoverflow.

1 Ответ

0 голосов
/ 27 апреля 2020

Моя проблема решена, хотя она не имеет ничего общего с 3 вопросами в посте. Я пытаюсь ответить на них в обратном порядке:

  • ad 3.b.) Если внешняя функция возвращается и ваша локальная лямбда была удалена, процессор выиграл ' Я знаю это и постараюсь обработать байты с этим смещением как инструкции процессора. Это, безусловно, испортит вам жизнь, так что никогда не делайте этого!

  • ad 3.a.) Да, безопасно уйти, если вы уверены на 100% внешняя функция никогда не вернется (в противном случае ваше приложение испортит

    1. , если вы удалили оставшуюся часть кода тем же способом, который описан в b.
    2. если вы не удалили лямбда-выражение или это глобальная функция, она запустит оставшуюся функцию, которая может редактировать динамически выделенную память c (куча, а не стек), которая была освобождена, и приведет к повреждению кучи или просто отредактируйте некоторые глобальные переменные).
  • ad 2.) Я погуглил опасные функции winapi и не нашел ничего, кроме TerminateThread(). Если вы знаете об этом, пожалуйста, добавьте комментарий или напишите другой ответ.

  • ad 1.) У меня нет никакого решения для проверки / исправления внутреннего Состояние kernel32 для процесса, на который ссылается Microsoft. Я думаю, что парень из Microsoft, который прочитал исходный код kernel32.dll, должен ответить на этот вопрос.

Помимо этого состояния kernel32, TerminateThread() вызовет множество других проблем (таких как resource / heap выделения, блокировки мьютексов, утечки и т. д.), поэтому никогда не используйте его, если вы не на 100% уверены, что делаете.

Прочитайте статью @RichardCritten, связанную в комментариях: TerminateThread()

В чем была ошибка в моем коде?

Я звонил TerminateThread() с небольшим тайм-аутом (300 мс). Случайно, когда у машины было мало ресурсов, функция все еще работала (я имею в виду неблокирующий вызов!). Я прекратил эту функцию и тем самым заблокировал мьютекс ядра. Этот заблокированный мьютекс заставил все остальные потоки ждать - и не завершать работу, когда они возвращались.

Замечания

Я ответил на свой вопрос из того, что нашел после того, как не получил никакого ответа. Следовательно, он может содержать некоторую неверную информацию. Пожалуйста, поправьте меня, если в этом что-то не так.

...