Подтвердить что ты не робот

C изменчивые переменные и кэш-память

Кэш контролируется аппаратным обеспечением кэширования прозрачно для процессора, поэтому, если мы используем переменные volatile в программе на C, как гарантируется, что моя программа каждый раз считывает данные с указанного фактического адреса памяти, но не кэша.

Я понимаю, что

  • Волатированное ключевое слово сообщает компилятору, что ссылки на переменные не должны быть оптимизированы и должны быть прочитаны как запрограммированные в коде.

  • Кэш контролируется аппаратным обеспечением кеша прозрачно, поэтому, когда процессор выдает адрес, он не знает, поступают ли данные из кеша или из памяти.

Итак, если у меня есть требование, чтобы каждый раз читать адрес памяти, как я могу убедиться, что его не отсылают из кеша, но из требуемого адреса?  

Некоторые, как, эти два понятия плохо подходят друг другу. Просьба уточнить, как это делается.

(Представьте, что у нас есть политика обратной записи в кеше (если это необходимо для анализа проблемы))

Спасибо, Микроядро:)

4b9b3361

Ответ 1

Разработчик прошивки здесь. Это стандартная проблема во встроенном программировании, а также одна из них, которая помогает многим (даже очень опытным) разработчикам.

Мое предположение заключается в том, что вы пытаетесь получить доступ к регистру оборудования, и это значение регистра может меняться со временем (будь то состояние прерывания, таймер, показания GPIO и т.д.).

Ключевое слово volatile является лишь частью решения, и во многих случаях может и не понадобиться. Это заставляет переменную перечитывать из памяти каждый раз, когда она используется (в отличие от оптимизации компилятором или хранения в регистре процессора для нескольких применений), но является ли считанная "память" фактическим аппаратным регистром по сравнению с кэшированным местоположением, неизвестно вашему коду и не подвержено влиянию ключевого слова volatile. Если ваша функция только считывает регистр один раз, вы можете, возможно, оставить volatile, но, как правило, я предлагаю, чтобы большинство аппаратных регистров определялось как volatile.

Большая проблема заключается в кешировании и кешировании. Самый простой способ заключается в том, чтобы убедиться, что ваш регистр находится в нераспространенном адресном пространстве. Это означает, что каждый раз, когда вы обращаетесь к регистру, вы гарантированно читаете/записываете фактический регистр оборудования, а не кэш-память. Более сложный, но потенциально более эффективный подход заключается в использовании кэшированного адресного пространства, и ваш код вручную принудительно обновляет кеш файлы для конкретных ситуаций, подобных этому. Для обоих подходов, как это делается, зависит от архитектуры и выходит за рамки вопроса. Это может включать MTRR (для x86), MMU, изменения в таблицах страниц и т.д.

Надеюсь, что это поможет. Если я что-то пропустил, дайте мне знать, и я расширю свой ответ.

Ответ 2

Мое предложение состоит в том, чтобы пометить страницу как не кэшированную диспетчером виртуальной памяти.
В Windows это делается с помощью PAGE_NOCACHE при вызове VirtualProtect.

Для несколько иной цели в инструкциях SSE 2 есть инструкции _mm_stream_xyz, чтобы предотвратить загрязнение кеша, хотя я не думаю, что они применимы к вашему делу здесь.

В любом случае нет портативного способа делать то, что вы хотите в C; вы должны использовать функциональность ОС.

Ответ 3

Из вашего вопроса есть заблуждение с вашей стороны.
Volatile ключевое слово не связано с кешем, как вы описываете.

Если для переменной указано ключевое слово Volatile, оно дает подсказку компилятору не выполнять определенные оптимизации, так как эта переменная может неожиданно изменяться с других частей программы.

Здесь имеется в виду, что компилятор не должен повторно использовать значение , уже загруженное в регистр, но снова получить доступ к памяти, так как значение в регистре не гарантируется таким же, как значение хранится в памяти.

Остальные, относящиеся к кеш-памяти, напрямую не связаны с программистом.

Я имею в виду, что синхронизация любой кэш-памяти CPU с ОЗУ - совсем другой предмет.

Ответ 4

В Википедии есть довольно хорошая статья о MTRR (регистры диапазона типов памяти), которые относятся к семейству процессоров x86.

Подводя итог этому, начиная с процессора Intel Pentium Pro (и с копией AMD), эти регистры MTR могли устанавливать в диапазонах памяти нераскрытые, записываемые, записываемые, записываемые, защищенные от записи или записываемые атрибуты.

Начиная с Pentium III, но, насколько я знаю, только очень полезно для 64-битных процессоров, они уважают MTRR, но их можно переопределить таблицами атрибутов страниц, которые позволяют процессору устанавливать тип памяти для каждой страницы памяти.

Основное использование MTRR, о котором я знаю, - это графическая RAM. Гораздо эффективнее отметить это как объединение записи. Это позволяет кешу хранить записи и отменяет все правила упорядочивания записи в памяти, чтобы обеспечить высокоскоростную запись на видеокарте.

Но для ваших целей вам понадобится либо MTRR, либо PAT-настройка как нераскрытого, так и сквозного.

Ответ 5

используя ключевое слово _Uncached, может помочь во встроенной ОС, например MQX

#define MEM_READ(addr)       (*((volatile _Uncached unsigned int *)(addr)))
#define MEM_WRITE(addr,data) (*((volatile _Uncached unsigned int *)(addr)) = data)

Ответ 6

Как вы говорите, кеш прозрачен для программиста. Система гарантирует, что вы всегда увидите значение, которое было записано в последний раз, если вы обращаетесь к объекту по его адресу. "Единственное", что вы можете понести, если устаревшее значение находится в вашем кеше, - это штраф за выполнение.

Ответ 7

volatile гарантирует, что данные считываются каждый раз, когда это необходимо, не беспокоясь о кеше между ЦП и памятью. Но если вам нужно прочитать фактические данные из памяти, а не кэшированные данные, у вас есть два варианта:

  • Создайте доску, где данные не кэшируются. Возможно, это может произойти, если вы обращаетесь к некоторому устройству ввода/вывода,
  • Используйте специальные инструкции CPU, которые обходят кеш. Это используется, когда вам нужно очистить память для активации возможных ошибок SEU.

Детали второй опции зависят от ОС и/или ЦП.