что такое volatile в си

Ключевое слово «volatile» C/C++

Оптимизация кода компилятором

«компилятор не будет оптимизировать эту переменную» — что означает оптимизировать? Наверное очень много людей, когда только начинали программировать задавались этим вопросом, не так ли? Думаю лучше продемонстрировать все на примерах, нежели рассказывать термины, которые большинству останутся не понятными.

Ну давайте начнем, к примеру имеем простой массив(правда не с простым размером), в цикле с которым выполняем какое-либо действие:

Самая затратная операция в этом примере не присваивание ячейке массива какого-либо значения и не инкремент счетчика, а именно операция сравнения, поэтому компилятор оптимизирует это примерно вот так:

Еще очень простой пример, в котором имеем массив символов, с помощью цикла проходим по всей строке и выполняем какие-то действия с символами:

В этом случае компилятор вынесет вызов strlen() в отдельную переменную:

Также чтобы не писать код, так как он очевиден, компилятор заменяет умножение на 2, сложением, но и пожалуй самый главный пример по нашей тематике, это то, что в большинстве случаев компилятор разгружает runtime программы, путем подстановки в выражения уже их значения, к примеру мы пишем программу для лифта. Одно из условий данной программы таково, что как только зайдут к примеру больше 4 человек должно выдаться предупреждение.

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

Источник

Ключевое слово volatile

Эта статья перевод этой статьи

Volatile

В ам когда-нибудь приходилось встречаться с подобными проблемами?

Если ваш ответ «да», то скорее всего вы не использовали служебное слово volatile в коде.

Вы не одиноки. Большинство программистов с трудом понимают приминение volatile. К сожалению, почти во всех книгах по си о volatile переменных рассказывается в паре предложений.

Служебное слово volatile в языке си используется при объявлении переменных. Оно указывает компилятору, что данная переменная может быть изменена в любое время, без каких либо действия со стороны кода, окружающего эту переменную. Последствия этого достаточно серьёзные. Но для начала, давайте рассмотрим синтаксис.

Синтаксис

Для объявления volatile переменной поместите служебное слово volatile до или после имени типа. К примеру, эти два объявления идентичны.

Довольно часто в программах используются указатели на volatile переменные, особенно для работы с портами ввода-вывода. К примеру, обе строчки кода объявляют указатель на беззнаковую volatile 8-битную переменную:

Volatile указатели на не-volatile переменные используются редко, но вот синтаксис:

Ну и для полноты, если вам нужен volatile указатель на volatile переменную

Между прочим, более полное объяснение того, почему volatile ставится после типа переменной (например, int volatile * foo) можно прочитать в колонке Dan Sak «Top-Level cv-Qualifiers in Function Parameters» (Embedded Systems Programming, February 2000, p. 63).

И наконец, если вы используете volatile для объявления экземпляра структуры или объединения, то всё содержимое структуры/объединения становится волатильным. Если такое поведение вам не нужно, то применяйте volatile к отдельным полям структуры/объединения.

Правильное использование служебного слова volatile

Переменная должны быть объявлена volatile, если она может быть внезапно изменена. На практике. Таким образом могут измениться только три типа переменных

Каждый из этих случаев рассмотрим в отдельной главе нашего повествования

Периферийные регистры

Встраиваемые системы включают в себя железо, обычно с довольно сложной периферией. Эти периферийные устройства могут менять состояние своих регистров асинхронно с работой программы. Для простого примера, представьте себе 8-битный регистр состояния, которые отображается на адрес 0x1234. Требуется проводить поллинг состояния до тех пор, пока в регистре не станет нулевое значение. Наивное, неправильное решение будет выглядеть так

Этот код почти со стопроцентной вероятностью отвалится после включения оптимизации, так как компилятор сгенерирует примерно такой код:

Логичное объяснение действия компилятора: мы уже прочитали значение в аккумулятор, нет нужды его опять читать, так как оно не изменяется (потому что никто не пишет в этот адрес памяти явно. прим. пер.). Из-за этого мы получаем бесконечный цикл. Для того, чтобы заставить компилятор делать то, что нам нужно, изменим объявление переменной

В итоге получим такой код на ассемблере

Мы достигли нужного нам результата.

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

Прерывания

Обработчики прерываний часто изменяют состояние переменных, которое проверяется в основной ветви кода. К примеру, обработчик прерываний на последовательном порту проверяет каждый новый символ на равенство EXT (скорее всего, он сигнализирует об окончании сообщения). Если символ равен EXT, то ISR выставляет глобальный флаг. Неверная реализация

Решение – объявить переменную ext_rcvd волатильной. Тогда все ваши проблемы (ну точно часть из них) исчезнут.

Многопоточные приложения

Несмотря на наличие очередей, пайпов и других поддерживаемых планировщиком способов взаимодействия в операционных системах реального времени, всё ещё довольно часто таски передают друг-другу информацию через общую область памяти (таким образом, глобальную). Даже если вы используете вытесняющий планировщик, компилятор ничего не знает о том, что такое переключение контекста, или когда оно может произойти. Таким образом, проблема любого таска, изменяющего общую глобальную переменную, принципиально не отличается от рассмотренной ранее проблемы с прерываниями. Таким образом, все глобальные переменные должны быть объявлены volatile. К примеру, этот код нарывается на неприятности:

Этот код скорее всего перестанет работать как надо, после включения оптимизаций. Объявление cntr волатильной правильный способ решить проблему.

Финальные размышления

Некоторые компиляторы позволяют вам неявно все переменные объявлять волатильными. Не впадите в искушение: по существу, это просто способ отключить мозг. Кроме того, код станет менее эффективным.

Также боритесь с искушением ругать компилятор и отключить оптимизатор. Современные оптимизирующие компиляторы настолько совершенны, что сложно вспомнить хоть один баг, вызванный их неправильной работой. А вот плохую работу программистов, не умеющих пользоваться volatile переменными, я встречаю угнетающе часто.

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

Источник

volatile (C++)

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

Синтаксис

Примечания

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

volatile Ключевое слово может не влиять на поле, если выполняется одно из следующих условий.

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

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

Соответствие стандартам ISO

Окончание соответствия ISO

Блок, относящийся только к системам Microsoft

Когда используется параметр компилятора /volatile: MS — по умолчанию, если нацелены архитектуры, отличные от ARM, компилятор создает дополнительный код для поддержания упорядочения между ссылками на переменные объекты, а также для поддержания порядка ссылок на другие глобальные объекты. В частности:

Запись в объект с ключевым словом volatile (т. н. «запись в изменяемый объект») имеет семантику освобождения. Это означает, что ссылка на глобальный или статический объект, которая находится в последовательности инструкций перед записью в объект с ключевым словом volatile, в скомпилированном двоичном файле будет находиться до записи в изменяемый объект.

Считывание из объекта с ключевым словом volatile (т. н. «считывание из изменяемого объекта») имеет семантику получения. Это означает, что ссылка на глобальный или статический объект, которая находится в последовательности инструкций после считывания из объекта с ключевым словом volatile, в скомпилированном двоичном файле будет находиться после считывания из изменяемого объекта.

Благодаря этому объекты с ключевым словом volatile могут использоваться для блокировки и освобождения памяти в многопоточных приложениях.

Завершение блока, относящегося только к системам Майкрософт

Источник

volatile для «чайников»

Виктор Тимофеев, июнь, 2010 osa@pic24.ru

Вступление

Определение

( volatile в переводе с английского означает «нестабильный», «изменчивый»)

Что это значит? Известно, что одной из характеристик компиляторов, говорящих за их качество, является способность оптимизировать генерируемый объектный код. Для этого они объединяют повторяющиеся конструкции, сохраняют в регистрах общего назначения промежуточные результаты вычислений, выстраивают последовательность команд так, чтобы минимизировать долго выполняющиеся фрагменты кода (например, обращение через косвенную адресацию), и т.д. Выполняя такую оптимизацию, они немного преобразует наш код, подменяя его идентичным с точки зрения алгоритма, но более быстрым и/или компактным. Но такую подмену можно делать не всегда. Рассмотрим пример:

С точки зрения алгоритма устанавливаются два младших разряда в переменной a. Оптимизатор может сделать подмену такого кода одним оператором:

выиграв таким образом пару тактов и пару ячеек ROM. Но представим себе, что эти же действия мы выполняем не над какой-то абстрактной переменной, а над периферийным регистром:

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

Ошибки, связанные с volatile

Есть три основных типа ошибок, касающихся квалификатора volatile :

Источник

Синтаксис

Ключевое слово volatile пишется до или после типа данных объявляемой переменной.

volatile int foo;
int volatile foo;

Указатели на volatile переменные объявляются так.

volatile int * pReg;
int volatile * pReg;

int * volatile p;

И, для полноты, если вам понадобится volatile указатель на volatile переменную, следует написать:

int volatile * volatile p;

Правильное использование спецификатора VOLATILE

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

1. Отображаемые в памяти периферийные регистры
2. Глобальные переменные, изменяемые в обработчике прерывания
3. Глобальные переменные, используемые в многопотоковом приложении

Далее мы поговорим о каждом из этих случаев.

Периферийные регистры

uint8_t * pReg = (uint8_t *) 0x1234;

// Wait for register to become non-zero
while (*pReg == 0) < >// Do something else

mov ptr, #0x1234
mov a, @ptr
loop:
bz loop

uint8_t volatile * pReg = (uint8_t volatile *) 0x1234;

Ассемблерный код теперь выглядит следующим образом:

mov ptr, #0x1234
loop:
mov a, @ptr
bz loop

Требуемый ход процесса достигнут.

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

Программа обработки прерывания (ISR)

int etx_rcvd = FALSE;

void main()
<
.
while (!etx_rcvd)
<
// Wait
>
.
>

interrupt void rx_isr( void )
<
.
if (rx_char == ETX)
<
etx_rcvd = TRUE;
>
.
>

Решение проблемы заключается в объявлении переменной etx_rcvd с ключевым словом volatile. После этого все ваши проблемы (ну или некоторые из них) исчезнут.

Многопотоковые приложения

int cntr;

void task1( void )
<
cntr = 0;

void task2( void )
<
.
cntr++;
sleep(10);
.
>

Заключительные размышления

Jones, Nigel. «Introduction to the Volatile Keyword» Embedded Systems Programming, July 2001

Related items

Comments

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

void PutChar(unsigne d char sym)
<
if (count Guest 2010-05-24 10:22

«очень длинный комментарий», бью на два сообщения:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *