2016-01-26 3 views
1

Я читал подобные ответы на этом сайте и в других местах, но в некоторых случаях все еще запутался.C volatile, и проблемы с аппаратным кэшированием

Я знаю, что стандарт на самом деле гарантирует нам, я понимаю предполагаемое использование ключевого слова, и я хорошо знаю разницу между кэшированием компилятора и L1/L2/ect. кэширование; это больше для любопытства, я понимаю другие случаи.

Скажем, у меня есть переменная, объявленная volatile в С. Четыре сценария:

  1. обработчики сигналов, однопоточный (Как и предполагалось): Это проблема ключевое слово должно было решить. Мой процесс получает обратный вызов сигнала от ОС, и я изменяю переменную volatile из обычного выполнения моего процесса. Поскольку он был объявлен volatile, нормальный процесс не сохранит это значение в регистре CPU и всегда будет выполнять загрузку из памяти. Даже если обработчик сигнала записывает в переменную volatile, так как обработчик сигнала имеет то же адресное пространство, что и обычный процесс, даже если переменная volatile была предварительно кэширована в аппаратном обеспечении (т.е. L1, L2), мы гарантируем, что основной процесс будет загрузите правильную обновленную переменную. Отлично, все счастливы.
  2. DMA-передачи, однопоточные: Говорят, что переменная volatile отображается в область памяти, для которой происходит запись DMA. Как и раньше, компилятор не будет хранить переменную volatile в регистре CPU и всегда будет выполнять загрузку из памяти; однако, если эта переменная существует в аппаратном обеспечении кеш, тогда запрос на загрузку никогда не достигнет основной памяти. Если контроллер DMA обновляет MM за спиной, мы никогда не получим обновленное значение. В упреждающей ОС нас спасает тот факт, что в конечном итоге мы, вероятно, будем отключать контекст, и в следующий раз, когда наш процесс возобновится, кеш будет холодным, и нам действительно придется перезагружать из основной памяти - так что мы получим правильную функциональность. В конечном итоге (наш собственный процесс также может поменять эту строку кэша), но опять же, мы можем потратить ценные циклы, прежде чем это произойдет). Существует ли стандартизованная поддержка HW или поддержка ОС, которая уведомляет аппаратные кэши, когда основная память обновляется через контроллер DMA? Или нам нужно явно очистить кеш, чтобы гарантировать, что мы не читаем ложное значение? (Это возможно даже в архитектур в списке?)
  3. отображенные в память регистры, однопоточный: То же, # 2, за исключением того, переменная volatile отображается в отображаемой памяти регистра (или явного ввода-вывода порта) , Я бы предположил, что это более сложная проблема, чем № 3, поскольку, по крайней мере, контроллер DMA будет сигнализировать CPU, когда он будет передан, что дает OS или HW возможность что-то сделать.
  4. Mutilthreaded: Если у меня есть переменная volatile, существует ли какая-либо гарантия согласованности кеша между несколькими потоками, работающими на отдельных физических ядрах? Как и вправду, компилятор все еще выдаёт инструкции загрузки из памяти, но если значение кэшируется в кеше одного ядра, есть ли какая-либо гарантия того же значения в кэшах другого ядра? (Я бы предположил, что это не проблема для гиперпотоков потоков на разных логических ядрах на одном физическом ядре, поскольку они разделяют физическую кэш-память). Моя подавляющая интуиция говорит «нет», но я подумал, что перечислил случай здесь.

Если возможно, проведите различие между архитектурами x64 и ARMv6/7/8, а также решениями ядра и пользователя.

+1

c/C++ 'volatile' не имеет ничего общего с оборудованием. Он просто отмечает переменную для компилятора. Компилятор не изменяет порядок доступа к отмеченным переменным и не оптимизирует доступ к ним. –

+0

@MichaelNastenko Мне показалось, что я знаю это. Может быть, я должен был понять, что я смотрю, есть ли доступная поддержка ОС/архитектуры, которая * могла бы быть использована компилятором за пределами стандарта C –

+1

'volatile' означает« без переупорядочения, без оптимизации ». Не больше, не меньше. # 1- # 3 сценарии, которые вы упомянули, решены аппаратными средствами. # 4 обычно решается MESI или любым другим протоколом когерентности кэша, а c/C++ 'volatile' бесполезен в этом случае. –

ответ

1

Для 2 и 3, нет стандартизованного способа, это сработает.

Как правило, при передаче DMA вы можете сбрасывать кеш в зависимости от платформы. Обычно для этого есть довольно прямые инструкции (с тех пор, как кэши встроены в CPU).

При обращении к регистрам с отображением памяти, с другой стороны, часто поведение зависит от порядка записи. Например, предположим, что у вас есть порт UART и пишите персонажи к нему - вам нужно убедиться, что каждый раз, когда вы пишете на него, каждый раз, когда вы пишете на него, должен быть указан файл.

Хотя он может работать с промывкой кеш между каждой записью, это не то, что обычно делает. Обычный способ (по крайней мере, для ARM) заключается в настройке MMU, чтобы записи в определенные области адресного пространства происходили без изменений и в правильной последовательности.

Этот подход также может использоваться для памяти, используемой для передачи DMA; можно было бы, например, настроить выделенные области для использования в качестве буферов DMA и настроить MMU так, чтобы чтение и запись в этот регион происходили без кэша.

С другой стороны, язык гарантирует, что вся память (ну, что вы получаете от объявления переменных или выделения памяти с использованием new) будет вести себя определенным образом. Не должно быть никакой разницы между многопоточным или имеющимися сигналами. Обратите внимание, что в стандартах C90 и C99 не упоминаются потоки (C11), но они должны работать таким образом. Реализация должна убедиться, что процессор и кеш используются таким образом, что это соответствует (как следствие, ОС не сможет планировать разные потоки на разных ядрах, если это невозможно). Следовательно, вам не нужно скрывать кеширование для обмена данными между потоками, но вам нужно синхронизировать потоки и, конечно же, использовать volatile квалифицированные данные. То же самое справедливо и для обработчиков сигналов, даже если реализация выполняется для их расписания на другом ядре.

+0

Благодарим за ответ - в частности, о кеш-флешах и MMU-отображаемых невосполнимых регионах DMA. Чтобы яснее прояснить последний абзац, я не считаю, что какой-либо стандарт C гарантирует глобальную видимость от записи до 'volatile'-памяти. Я думаю, что нам просто повезло с этим на x86 из-за того, как он синхронизирует свои кеши. –

+0

c/C++ 11 добавлена ​​библиотека атомных операций для кросс-платформенной многопоточности. –

+1

@ jammie Да, стандарт C, похоже, не охватывает потоки. Однако следует иметь в виду, что любая реализация потоков ведет себя таким образом (можно утверждать, что это лежит в концепции потоков). Если на платформе имеются отдельные ядра с отдельными кешами, следует учитывать это при разработке функций потоковой обработки, иначе это может стать совершенно непригодным. – skyking

Смежные вопросы