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. Точно так же не работает моя система 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, и это не имеет значения.

Все еще заинтересован в объяснении, но, по-видимому, это не является источником моего случайного сбоя переключения контекста.


person Ira Baxter    schedule 01.04.2014    source источник
comment
+1 просто за ошибку .. «мужество и храбрость».   -  person Martin James    schedule 01.04.2014
comment
Проверьте, установлен ли CONTEXT_CONTROL (бит 0) в поле ContextFlags.   -  person Igor Skochinsky    schedule 01.04.2014
comment
@MartinJames: на самом деле это система выполнения для параллельного языка программирования. Переключение контекста обеспечивает некоторую степень справедливости для многих логических фрагментов выполнения, мультиплексируемых планировщиком гранул поверх доступных ему потоков 1-на-ЦП, благодаря ОС MS. Да, это раздвигает границы разумного :-} См. этот вопрос для других сумасшедших поведений MS, с которыми я столкнулся при создании механизмов параллельного выполнения: stackoverflow. com/questions/9461559/   -  person Ira Baxter    schedule 01.04.2014
comment
@IgorSkochinsky: Вы заставили меня :-} добавить код переключения контекста в вопрос. Да, флаг CONTROL_CONTROL есть. Спасибо за нестандартное мышление. Еще об этом, пожалуйста!   -  person Ira Baxter    schedule 01.04.2014
comment
Кстати, вы заново изобретаете волокна?   -  person Igor Skochinsky    schedule 01.04.2014
comment
Нет. Я не думаю, что вы можете разрезать волокно по времени (ну, я полагаю, что с помощью этой техники можно, но MS этого не предлагает). Волокна (AFAIK) нельзя украсть, чтобы запустить на бездействующем ЦП, что делает наша реализация параллельного языка. Волокна не могут ждать других волокон. Волокна не могут прерывать друг друга. Файберам по-прежнему приходится жить с большим стеком окон (см. stackoverflow.com/a/1053159/120163). Я думаю, что делаю то, на что волокна действительно не способны.   -  person Ira Baxter    schedule 01.04.2014
comment
Downvoter: не могли бы вы объяснить, почему вы проголосовали против.   -  person Ira Baxter    schedule 01.04.2014
comment
Если вы используете отладчик ядра и используете команду .thread для установки контекста регистра для рассматриваемого потока, согласуется ли дамп регистра команды r с тем, что вы ожидаете, или с тем, что вы получаете от GetThreadContext()?   -  person Michael Burr    schedule 02.04.2014
comment
@MichaelBurr: я никогда не использовал отладчик ядра; Я держусь подальше от ядра или чего-то подобного. Поэтому я не могу ответить на ваш вопрос. В качестве мысленного эксперимента я ожидаю, что это сработает просто отлично, особенно статистически. Частота отказов, которую я вижу, составляет примерно 1 из 10 000 событий переключения контекста. Более интересным экспериментом (может быть, тот, который вы задумали?) было бы провести этот эксперимент, когда я достигну точки останова, посмотреть, не получу ли я там бессмысленное поведение. Как узнать об отладчике ядра?   -  person Ira Baxter    schedule 02.04.2014
comment
@IraBaxter: Средства отладки для Windows теперь являются частью установки SDK (вы можете выбрать отладчики конкретно — вам не нужно устанавливать весь SDK): msdn.microsoft.com/en-us/library/windows/hardware/ff551063.aspx Вы можете получить информация о состоянии регистра потока с использованием одного из стандартных отладчиков cdb, ntsd или windbg вместо отладчика ядра kbd (или windbg также может использоваться как отладчик ядра). Инструменты отладки поставляются с отличным файлом справки debugger.chm, который стоит прочитать.   -  person Michael Burr    schedule 02.04.2014
comment
В книгах Руссиновича «Внутри Windows» содержится отличная информация о том, как использовать инструменты отладки для поиска информации на системном уровне. На его сайте sysinternals также есть livekd инструмент, позволяющий выполнять некоторые ограниченные действия. отладка ядра в «живой системе» без необходимости устанавливать последовательный, USB или Firewire-канал между хостом и целью, как вы обычно делаете для отладки ядра. Другой альтернативой является использование гостя VMware в качестве цели отладки ядра: msdn.microsoft.com/en-us/library/windows/hardware/ff538143.aspx   -  person Michael Burr    schedule 02.04.2014
comment
Получаете ли вы такое же поведение на реальном оборудовании x86? Я определенно видел, как эмуляторы позволяли себе вольности с различными флагами регистров.   -  person josh poley    schedule 02.04.2014
comment
Не знаю, что делает реальное оборудование; мне трудно до него добраться, и проблема в том, что этот бит ошибается лишь раз за долгое время. Вам нужен статистический тест. Да, я видел MS, пытающихся эмулировать ОС, допуская всевозможные вольности. (Не заставляйте меня начинать, SuspendThread может сказать: «Упс, я не делал этого для работающего потока».)   -  person Ira Baxter    schedule 02.04.2014


Ответы (1)


Microsoft имеет давнюю историю хранения нескольких битов в местах, которые на самом деле не используются. Рэймонд Чен привел множество примеров, например. используя младшие биты указателя, который не выровнен по байтам.

В этом случае Windows, возможно, потребовалось сохранить часть контекста своего потока в существующей структуре CONTEXT, и она решила использовать неиспользуемый бит в EFLAGS. Вы все равно ничего не могли сделать с этим битом, и Windows вернет этот бит, когда вы вызовете SetThreadContext.

person MSalters    schedule 02.04.2014
comment
Итак, вот хрупкая дизайнерская идея. Сохраните недокументированный бит, критический для правильной работы потоков, в неиспользуемом бите регистра EFLAGS в блоке CONTEXT. Предположим, что пользовательская программа не изменит его. (MS не делает такого предположения ни для какого другого регистра в блоке CONTEXT, ни для любого из битов в EFLAG, измененных процессором: Z,O,P,N,DIR,... я знаю, потому что я меняю эти много и все работает нормально). Теперь пользователь изменяет этот критический бит; критическая функция теперь должна завершиться недокументированным образом. Если бы у меня был программист, который это делал, я бы его расстрелял. - person Ira Baxter; 02.04.2014
comment
... Многие люди используют младшие биты указателей, не выровненных по байтам, для вещей. Это по крайней мере очевидно (ненулевые младшие биты) и это нормально, если задокументировано. Это кажется совсем другим. [Не поймите меня неправильно; Я ценю ваши отзывы]. - person Ira Baxter; 02.04.2014