Я использую Win32 API для остановки/запуска/проверки/изменения состояния потока. Вообще неплохо работает. Иногда это не удается, и я пытаюсь отследить причину.
У меня есть один поток, который принудительно переключает контекст на другие потоки:
thread stop
fetch processor state into windows context block
read thread registers from windows context block to my own context block
write thread registers from another context block into windows context block
restart thread
Это работает на удивление хорошо... но... очень редко переключение контекста дает сбой. (Симптом: моя многопоточная система взлетает до небес, выполняя странные места со странным содержимым регистров).
Контекстный контроль осуществляется с помощью:
if ((suspend_count=SuspendThread(WindowsThreadHandle))<0)
{ printf("TimeSlicer Suspend Thread failure");
...
}
...
Context.ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_FLOATING_POINT);
if (!GetThreadContext(WindowsThreadHandle,&Context))
{ printf("Context fetch failure");
...
}
call ContextSwap(&Context); // does the context swap
if (ResumeThread(WindowsThreadHandle)<0)
{ printf("Thread resume failure");
...
}
Ни один из операторов печати никогда не выполняется. Я пришел к выводу, что Windows считает, что все контекстные операции выполняются надежно.
О, да, я действительно знаю, когда останавливаемый поток не выполняет вычисления [например, в системной функции] и не будет пытаться его остановить или переключить контекст. Я знаю это, потому что каждый поток, который выполняет что-либо, кроме вычислений, устанавливает для конкретного потока флаг «не трогать меня», пока он занимается чем-то другим, кроме вычислений. (Программисты драйверов устройств распознают это как эквивалент инструкций «отключения прерывания»).
Итак, я задался вопросом о достоверности содержимого контекстного блока. Я добавил множество тестов работоспособности для различных значений регистров, извлеченных из контекстного блока; на самом деле вы можете решить, что ESP в порядке (в пределах области стека, определенной в TIB), ПК находится в программе, которую я ожидаю, или в системном вызове и т. д. Здесь нет никаких сюрпризов.
Я решил проверить, правильно ли считываются биты кода состояния (EFLAGS); если бы это было неправильно, это привело бы к тому, что переключенная задача выбрала «неправильную ветвь» при восстановлении ее состояния. Поэтому я добавил следующий код, чтобы убедиться, что предполагаемый регистр EFLAGS содержит данные, которые только выглядят как EFLAGS согласно справочному руководству Intel (http://en.wikipedia.org/wiki/FLAGS_register).
mov eax, Context.EFlags[ebx] ; ebx points to Windows Context block
mov ecx, eax ; check that we seem to have flag bits
and ecx, 0FFFEF32Ah ; where we expect constant flag bits to be
cmp ecx, 000000202h ; expected state of constant flag bits
je @f
breakpoint ; trap if unexpected flag bit status
@@:
На моем Win 7 AMD Phenom II X6 1090T (шестнадцатеричное ядро) иногда возникают ловушки с точкой останова с ECX = 0200h. Точно так же не работает моя система Intel i7 Win 7. Я бы проигнорировал это, за исключением того, что это намекает на то, что EFLAGS не сохраняются правильно, как я и подозревал.
Согласно моему чтению справочных руководств Intel (а также AMD), бит 1 зарезервирован и всегда имеет значение «1». Не то, что я вижу здесь.
Очевидно, MS заполняет блок контекста, выполняя сложные действия при остановке потока. Я ожидаю, что они будут точно хранить состояние. Этот бит хранится неправильно. Если они не хранят этот бит правильно, что еще они не сохраняют?
Любые объяснения, почему значение этого бита иногда может/должно быть равно нулю?
РЕДАКТИРОВАТЬ: мой код сбрасывает регистры и стек при обнаружении точки останова. Область стека содержит блок контекста как локальную переменную. И EAX, и значение в стеке по правильному смещению для EFLAGS в блоке контекста содержат значение 0244h. Таким образом, значение в блоке контекста действительно неверно.
EDIT2: я изменил значения маски и сравнения на
and ecx, 0FFFEF328h ; was FFEF32Ah where we expect flag bits to be
cmp ecx, 000000200h
Кажется, работает надежно, без нареканий. По-видимому, Win7 неправильно выполняет бит 1 eflags, и это не имеет значения.
Все еще заинтересован в объяснении, но, по-видимому, это не является источником моего случайного сбоя переключения контекста.
CONTEXT_CONTROL
(бит 0) в полеContextFlags
. - person Igor Skochinsky   schedule 01.04.2014.thread
для установки контекста регистра для рассматриваемого потока, согласуется ли дамп регистра командыr
с тем, что вы ожидаете, или с тем, что вы получаете отGetThreadContext()
? - person Michael Burr   schedule 02.04.2014cdb
,ntsd
илиwindbg
вместо отладчика ядраkbd
(илиwindbg
также может использоваться как отладчик ядра). Инструменты отладки поставляются с отличным файлом справкиdebugger.chm
, который стоит прочитать. - person Michael Burr   schedule 02.04.2014livekd
инструмент, позволяющий выполнять некоторые ограниченные действия. отладка ядра в «живой системе» без необходимости устанавливать последовательный, USB или Firewire-канал между хостом и целью, как вы обычно делаете для отладки ядра. Другой альтернативой является использование гостя VMware в качестве цели отладки ядра: msdn.microsoft.com/en-us/library/windows/hardware/ff538143.aspx - person Michael Burr   schedule 02.04.2014