sysel 0 2 октября, 2009 Опубликовано 2 октября, 2009 · Жалоба Здравствуйте! Прошу прощения за оффтоп(хотя ОС Windows CE), но проблема такова: Пишу многопоточную программу под х86 процессор (защищенный режим) на С++. Есть глобальная переменная (назовём её Count) для взаимодействия двух потоков. (в обоих потоках присутствуют операции чтение, запись и чтение-модификация-запись (Count=Count+A)). По логике программы операции чтение и запись не могут привести к нарушению работы, а вот чтение-модификация-запись - может. Возникла идея реализовать операцию Count=Count+A в виде ассемблерной вставки, где операция выполняется инструкцией Add: __asm{ push eax mov eax,A add Count,eax pop eax }; , т.к. код, генерируемый компилятором по умолчанию сводился к: mov eax,Count mov ebx,A add eax,ebx ; опасная строка 1 mov Count,eax ; опасная строка 2 Все эти заморочки вызваны тем, что в любом месте моя программа может быть прервана планировщиком ОС. Т.е. если планировщик вклиниться между опасными командами 1 и 2, запустит на выполнение второй поток, который выполнит опасные команды 1 и 2, и опять запустит на выполнение первый поток, который выполнит опасную команду 2, возникнет ситуация "пепец". Теперь внимание вопрос: если операция Count=Count+A в ассемблерном коде представлена одной процессорной командой Add, гарантирует ли это отсутствие ситуации "пепец" ? Иными словами, можно ли ассемблерную инструкцию считать атомарной (выполняемой как единое целое) ? Иными словами: планировщик современных ОС вклинивается между инструкциями x86 или между инструкциями внутреннего RISC-ядра процессора? На всякий случай добавлю: пользоваться объектами синхронизации ОС (критические секции, Interlocked...) не вышло - очень замедляет работу моего RealTime приложения. Приложение запущено в пользовательском режиме (кольцо 3), cli и sti не советовать. На всякий случай доп. информация (скорее всего не существенно): Среда: Visual Studio 2005 ОС(целевая): Windows CE 6.0 R2 Процессор(целевой): AMD Geode LX800 (x86(набор инструкций P5), FPU, MMX, 3DNow!) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KolyanV 0 4 октября, 2009 Опубликовано 4 октября, 2009 · Жалоба Команды процессора с точки зрения прерываний - атомарны. Т.е начатая команда обязательно будет выполнена до конца. Правда с учетом конвеерной обработки, не все так гладко. Возможно, завершение выполнения команды будет происходить тогда, когда процессор начнет вход в прерывание. Но, я думаю, это не должно волновать, так как завершение будет занимать доли/единицы тактов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Xenia 35 4 октября, 2009 Опубликовано 4 октября, 2009 · Жалоба Если ваш код работает в защищенном режиме, то отчего бы не использовать команды запрета и разрешения прерываний? Вот так: __asm{ push eax cli mov eax,A add Count,eax sti pop eax }; Ведь любой планировщик получает свою "жизненную силу" от прерывания, а раз программу в этом месте нельзя прервать, то никакой планировщик туда не вклинится. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
readt 0 4 октября, 2009 Опубликовано 4 октября, 2009 · Жалоба Все эти заморочки вызваны тем, что в любом месте моя программа может быть прервана планировщиком ОС. Т.е. если планировщик вклиниться между опасными командами 1 и 2, запустит на выполнение второй поток, который выполнит опасные команды 1 и 2, и опять запустит на выполнение первый поток, который выполнит опасную команду 2, возникнет ситуация "пепец". Копать нужно в сторону примитивов взаимоисключений. Описанные проблемы с разделением ресурса несколькими потоками не новы. Вон у классика Дейтела описано несколько алгоритмов. Под win я бы использовал объекты диспетчеризации ядра (семафоры, мьютексы и проч.). А для защищенного режима попробуйте команду BTS - Bit Test and Set (386+), вот она и является атомарной. Правильному планировщику должно быть по боку, когда он забирает процессор у потока. Другими словами: отработал квант времени - вон с пляжа. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DimaG 0 5 октября, 2009 Опубликовано 5 октября, 2009 · Жалоба Команды процессора с точки зрения прерываний - атомарны. Т.е начатая команда обязательно будет выполнена до конца. Правда с учетом конвеерной обработки, не все так гладко. Возможно, завершение выполнения команды будет происходить тогда, когда процессор начнет вход в прерывание. Но, я думаю, это не должно волновать, так как завершение будет занимать доли/единицы тактов. В свое время на BlackFin 537 столкнулся с следующей проблемой: * * * * volatile int* piReg_; * * * * *piReg_ = /* доступ к внешней памяти */ * * * Так вот: если во время доступа к внешней памяти происходило прерывание, то вместо одного чтения из памяти, иногда получалось 2. К сожалению в причинах не разобрался - просто на время доступа стал отключать прерывания. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KolyanV 0 6 октября, 2009 Опубликовано 6 октября, 2009 · Жалоба В свое время на BlackFin 537 столкнулся с следующей проблемой: .... Так вот: если во время доступа к внешней памяти происходило прерывание, то вместо одного чтения из памяти, иногда получалось 2. К сожалению в причинах не разобрался - просто на время доступа стал отключать прерывания. Ничего не скажу по архитектуре блекфина, но приведенный Вами фрагмент мало о чем говорит. Насколько атомарна выполняемая там операция зависит от того, как компилятор его переведет в машиный код. В случае, приведенном топикстартером модификация ячейки памяти выполняется одной командой (add Count,eax) и поэтому она атомарна (во всяком случае в архитектуре x86). 2 xenia: Никаких cli / sci в коде прикладных программ под ОС высокого уровня Windows/Linux быть не может, и это правильно - времена ДОСов прошли. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Xenia 35 6 октября, 2009 Опубликовано 6 октября, 2009 · Жалоба 2 xenia: Никаких cli / sci в коде прикладных программ под ОС высокого уровня Windows/Linux быть не может, и это правильно - времена ДОСов прошли. Не скажите :). Вы лично пробовали CLI/STI под WinXP или Vista? Да, это привилегированные команды, которые процессор выполняет только в ядре (0-ом кольце защиты), а в приложении они вызывают "исключение", т.е. прерывание по случаю попытки нарушения защиты. Абортируют ли эти команды приложение? А вот и нет! Исключение и в самом деле происходит, однако супервизор решает, что наружение можно простить :). Т.е. проверив инструкцию, вызвавшую прерывание (это делается легко, т.к. когда происходит прерывание или исключение, ее адрес лежит на стеке), адрес возврата будет увеличен на единичку, благодаря чему управление вернется на инструкцию, следующую за CLI/STI. В результате выполнение приложения продолжится, как будто этих инструкций не было. Однако то, что я описала - это только механизм "прощения" такого нарушения, а в реальности супервизор может перед "прощением" повысить приоритет потоку, вызвавшему такое исключение, если нарушившая порядок инструкция была CLI. Или вернуть потоку нормальный приоритет, если это была инструкция STI. Как видите, под управлением современных ОС высокого уровня, примитивы CLI/STI тоже могут работать, только не напрямую, а опосредованно. И это, между прочим, решает массу проблем! Например, аппаратные прерывания от таймера, мыши, DMA и прочего будет продолжать работать, а процесс вытеснения потоков и задач может быть временно приостановлен. Хотя он будет все равно запущен, если STI долго не придет. Другое дело, что опираться на подобные механизмы можно только тогда, когда в точности знаешь реакцию данной ОС на CLI/STI. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ReAl 0 7 октября, 2009 Опубликовано 7 октября, 2009 · Жалоба Другое дело, что опираться на подобные механизмы можно только тогда, когда в точности знаешь реакцию данной ОС на CLI/STI.И работать, мне кажется, они будут медленнее Interlocked*, уже "забаненных" в первом сообщении за свою за неторопливость. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DimaG 0 7 октября, 2009 Опубликовано 7 октября, 2009 · Жалоба Ничего не скажу по архитектуре блекфина, но приведенный Вами фрагмент мало о чем говорит. Насколько атомарна выполняемая там операция зависит от того, как компилятор его переведет в машиный код. Переменная объявлена как volatile. Предпологаю, что процессор не имеет права сгенерить несколько машинных инструкций чтения из внешней памяти для одной Си операции чтения. А то факт, что вместо одного чтения из внешней памяти происходило два - могу объяснить только прерыванием текущей операции чтения из внешней памяти, выполнением ISR, и повторным выполнением операции чтения Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Oldring 0 7 октября, 2009 Опубликовано 7 октября, 2009 · Жалоба Пишу многопоточную программу под х86 процессор (защищенный режим) на С++. Есть глобальная переменная (назовём её Count) для взаимодействия двух потоков. (в обоих потоках присутствуют операции чтение, запись и чтение-модификация-запись (Count=Count+A)). Под Виндами правильно пользоваться Interlocked функциями Win32 API, такими как InterlockedIncrement. Код будет гарантированно переносимыми на другие платформы. Ассемблерная команда инкремента переменной в памяти будет атомарной с точки зрения ядра процессора, но не с точки зрения доступа по шине. Поэтому предлагаемое решение не будет работать на многоядерных процессорах. Правильное решение на x86 ассемблере - использовать команду с префиксом lock. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Xenia 35 7 октября, 2009 Опубликовано 7 октября, 2009 · Жалоба Правильное решение на x86 ассемблере - использовать команду с префиксом lock. Что-то сомнительно. Префикс LOCK блокирует шину только на время выполнения данной инструкции, поэтому здесь это не поможет, поскольку "порча" происходит во время прерывания между соседними инструкциями. Кроме того, у меня противоречивые сведения о том, распространяются ли привилегии на префикс LOCK или нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Oldring 0 7 октября, 2009 Опубликовано 7 октября, 2009 · Жалоба Что-то сомнительно. Префикс LOCK блокирует шину только на время выполнения данной инструкции, поэтому здесь это не поможет, поскольку "порча" происходит во время прерывания между соседними инструкциями. Кроме того, у меня противоречивые сведения о том, распространияются ли привилегии на префикс LOCK или нет. Инструкция, разумеется, должна быть одна, а именно, инкремента ячейки памяти командами lock add или lock xadd. Более сложные атомарные конструкции всегда можно сконструировать при помощи lock cmpxchg и цикла. См. IA32 Instruction Set Reference Кстати, борющимся за скорость будет полезно ознакомиться вот с этой статьей из MSDN http://msdn.microsoft.com/en-us/library/2ddez55b(VS.71).aspx Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться