x86 зарезервированные EFLAGS, бит 1 == 0: как это может произойти?
Я использую 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. Сбой так же на моей системе Win 7 Intel i7. Я бы проигнорировал это, за исключением того, что он намекает, что 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 правильно, и, похоже, это не имеет значения.
Все еще интересуются объяснениями, но, очевидно, это не источник моего случайного сбоя переключения контекста.
1 ответ
У Microsoft долгая история создания нескольких кусков в местах, которые на самом деле не используются. Раймонд Чен привел множество примеров, например, используя младший бит (ы) указателя, который не выровнен по байту.
В этом случае Windows, возможно, нужно было сохранить часть контекста потока в существующем CONTEXT
структура, и решил использовать неиспользованный бит в EFLAGS
, В любом случае, вы ничего не можете сделать с этим битом, и Windows вернет этот бит, когда вы позвоните SetThreadContext
,