О volatile замолвлю я слово

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
SfS
Друг Кота
Сообщения: 19756
Зарегистрирован: Пт янв 12, 2007 11:21:39
Откуда: Томск

Сообщение SfS »

О volatile замолвлю я слово
volatile.pdf
(25.53 КБ) 173 скачивания
Глядя на полемику по поводу использования ключевого слова volatile, возникшую в https://radiokot.ru/forum/viewtopic.php ... start=7140, я, признаться, был обескуражен той лёгкостью подхода, с которой некоторые авторы навязывают свою точку зрения и учат молодёжь дурному: не использовать это слово, использовать какие-то «ключи оптимизации» вместо него и т.д. и т.п.
В скобках отмечу, что исходя из своего опыта преподавания программирования микроконтроллеров, я знаю, что ровно 100% студентов налетают на «грабли» с volatile. И это — неспроста.
Всё дело в непонимании для чего оно нужно и зачем его использовать.

Итак, глянем, что говорится о ключевом слове volatile в документации к языкам С и С++. Я не буду приводить километровых цитат, а только приведу основной смысл. Итак:

Код: Выделить всё

		Ключевое слово volatile информирует компилятор, что значение переменной может меняться извне.
Что это значит на практике? А именно то, что сказано. То есть, если переменная обозначена как volatile, то её значение может произвольно меняться в любой момент времени.
Что из этого следует? А следует из этого простой вывод: мы не можем гарантировать, что переменная в следующий момент времени будет иметь то же значение, что и в предыдущий.
Поэтому, компилятор, встречая volatile-переменную «понимает», что любое действие с ней (а это лишь два возможных действия — чтение и запись) — следует производить в точности как указал программист, без всяких оптимизаций.

Преимущества слова volatile перед многомудрыми советами «использовать ключи оптимизации», «прагмы» и «атрибуты» и прочими «а у меня и так всё работает» - очевидны:

1. Слово volatile — это стандарт. То есть его обязан понимать и использовать любой компилятор C/C++. Вывод — программа более переносима.
2. Ключи оптимизации распространяются на отдельный файл. Слово volatile — распространяется на отдельную переменную или поле структуры. То есть, с точки зрения оптимизации, за которую боролись сторонники «ключей оптимизации, прагм и атрибутов», именно слово volatile позволяет выполнить более точную оптимизацию программы.
3. Ключи компиляции привязаны к конкретному компилятору. Отсюда непереносимость программы. Кроме того, выставлять в программе из десятков, а то и сотен файлов индивидуальные ключи оптимизации для каждого из файлов — это очень на любителя извращений.
4. Прагмы и атрибуты ещё более, чем ключи оптимизации, привязаны к конкретному компилятору. Отсюда ещё большая непереносимость программы.

Итак, volatile — это стандарт и переносимость плюс облегчение себе жизни в плане экспериментов с ключами оптимизации.

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

А теперь — слайды! Ну то есть примеры — зачем и для чего нужно использовать volatile.

Запись в аппаратный регистр
Итак, предположим, некто В.Пупкин начал писать программу. И есть у него байтовый регистр UART FIFO DATA, выглядящий для микроконтроллера как ячейка памяти, расположенная по адресу 0x010A Запись в этот регистр приводит к записи байта в FIFO UART, а чтение из него, соответственно к чтению из FIFO UART.
В.Пупкин, начитавшийся советов о том, что volatile есть зло, уверенно пишет тест-программу (инициализацию UART опустим):

Код: Выделить всё

unsigned char* udr = (unsigned char*)0x010A;
*udr = 'A';
и с любовью во взоре наблюдает буковку 'A' на экране терминала. Затем, В.Пупкин, как истинный говноайтишнег решает вывести своё имя на экран:

Код: Выделить всё

unsigned char* udr = (unsigned char*)0x010A;
*udr = 'В';
*udr = 'а';
*udr = 'с';
*udr = 'я';
И что он видит? А видит он только букву 'я' — последнюю букву. И начинает рвать волосы на теле, если таковые имеются, ругать землю, небо, страну, террана и вообще все на свете. Но, как обычно, виноват сам В.Пупкин.
Рвёт волосищи и ругается он до тех пор, пока не докладывается, что надо бы почитать умных людей и понять — почему от его имени осталась последняя буква.
Объяснение просто. Многие компиляторы, даже при отключённой оптимизации, кое-какую оптимизацию всё же производят. И компилятор, абсолютно законно, решает, что если в одну и ту же переменную производится несколько записей подряд, то он вправе выкинуть все операции записи, кроме последней. А что? Результат то не меняется. Для обычной переменной, разумеется, не меняется. Но у нас то переменная необычная.
Совершенно меняется результат выполнения кода, если написать:

Код: Выделить всё

volatile unsigned char* udr = (unsigned char*)0x010A;
*udr = 'В';
*udr = 'а';
*udr = 'с';
*udr = 'я';
На экране появится имя 'Вася' и наш программист будет удовлетворён полностью. Почему так произойдёт — понять не сложно. Раз переменная помечена как volatile — то запись в неё будет производиться каждый раз, вне зависимости от того, что там кажется компилятору.
Пример этот не такой надуманный как кажется. Буфера FIFO в UART и не только в UART - это вещь распространённая.

Чтение из аппаратного регистра
Предположим, что наш В.Пупкин опрашивает состояние входа GPIO и ждёт нажатия кнопки. Пусть регистр состояния GPIO байтовый и расположен как ячейка памяти по адресу 0x0507. Проверять будем бит 3. Пусть при нажатой кнопке в бит 3 выставляется «нуль», а если кнопка не нажата, то в бите 3 будет «единица».

Код: Выделить всё

unsigned char* gpio = (unsigned char*)0x0507; 
while( (*gpio) & (1<<3) ) {} // Ждём нажатия кнопки
Тут сложно точно сказать, что произойдёт. Но многие компиляторы соптимизируют этот цикл до однократного чтения из регистра gpio и затем уйдёт в бесконечный цикл, если в момент опроса кнопка не нажата.
И опять же — запись:

Код: Выделить всё

volatile unsigned char* gpio = (unsigned char*)0x0507; 
while( (*gpio) & (1<<3) ) {} // Ждём нажатия кнопки (бит 3 == 0)
решит проблему опроса регистра. Опрос регистра gpio будет производиться каждую итерацию цикла.

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

Код: Выделить всё

// Переменная хранит посленее считанное значение из UART
char udr_data;

// Прерывание по приходу байта в UART
ISR_UART_RECV(){
		udr_data = UDR; /* читаем данные в переменную */
}

// Какойто основной цикл
..
udr_data=0;
while(1) {
		switch(udr_data) {
			case '1':
				// Пришло 1 — что-то делаем
			break;
			case '2':
				// Пришло 2 — что-то делаем
			break;
		}
}
То есть идёт постоянная проверка значения переменной udr_data и в зависимости от значения в ней производится какое-то действие. Всё логично. С первого взгляда.
Но… работать такой код часто не будет. Многие компиляторы вообще выкинут все тело switch-case потому как решат, что если переменной присвоен 0 — то она никогда не станет равной '1' или '2'. И, следовательно, зачем лишний код? Мёртвый цикл лучше. И короче.
Точно так же тут поможет объявление переменной как volatile:

Код: Выделить всё

// Переменная хранит посленее считанное значение из UART
volatile char udr_data;
В этом случае switch-case будет работать как надо — каждый раз проверяя значение переменной udr_data.
Заключение
Разумеется, что во всех приведённых примерах можно было бы добиться работоспособности кода и без volatile.
Но только для этого надо было бы написать примеры для каждого конкретного компилятора: GCC, IAR, KEIL и других.
А вот ключевое слово volatile — универсально и бьёт точно в цель — то есть в ту переменную, которая особенная и не задевает код, не относящейся к другим объектам.
Если вы загляните в исходник библиотек для микроконтроллеров, то увидите, что все регистры там объявлены как volatile. И это не с проста.
Проще всегда делать правильно, чем сэкономить десяток байт кода, но постоянно рисковать нарваться на грабли. Адепты мантры - «а у меня и так работает» - часто нарываются на очень неожиданное поведение кода в самый неподходящий момент времени. Удачи им в скачке по граблям.
Разумеется, что использовать ключевое слово volatile нужно с умом, то есть применять его только к тем переменным, которые либо изменяются аппаратно (как на чтение, так и на запись), либо модифицируются в прерывании, а проверяются вне его.
Конечно, бывают и более сложные случаи. Но нельзя объять необъятного и потому думайте сами, где надо ставить volatile, а где оно лишнее.
Счастливой разработки!
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2516
Зарегистрирован: Пт июл 12, 2019 22:52:01

Сообщение Eddy_Em »

Это что ж за осел про некие ключи компиляции говорил? Что за ключи такие, которые позволяют часть переменных волатильными обозначить?
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда.
Я на гитхабе, в ЖЖ
Контактная информация:
Реклама
SfS
Друг Кота
Сообщения: 19756
Зарегистрирован: Пт янв 12, 2007 11:21:39
Откуда: Томск

Сообщение SfS »

[uquote="Eddy_Em",url="/forum/viewtopic.php?p=4162035#p4162035"]Это что ж за осел про некие ключи компиляции говорил? Что за ключи такие, которые позволяют часть переменных волатильными обозначить?[/uquote]

Не часть, а все, как я понял. Оптимизацию полностью отключить:)

Но там было больше "а у меня и так все работает".
Друг Кота
Аватара пользователя
Сообщения: 6823
Зарегистрирован: Сб янв 28, 2006 22:47:24

Сообщение Asmodey »

[uquote="SfS",url="/forum/viewtopic.php?p=4162026#p4162026"]И что он видит? А видит он только букву 'я' — последнюю букву. И начинает рвать волосы на теле, если таковые имеются, ругать землю, небо, страну, террана и вообще все на свете.[/uquote]
Мало того, Вася в ряде случаев не сможет проконтролировать вожделенную переменную в окне окне отладки, если она не volatile. Ну не появляется в окошке с переменными и все тут. Поэтому приходится волатилить даже там, где вроде нужды особой нет, лишь бы отладку сделать чуть более комфортной. Вот и пишешь volatile, а сам думаешь - где этот модификатор мне свинью подложит в будущем? Ведь более опытные товарищи то тут, то там пишут - volatile опасносте. Но, редко пишут в чем же проявляются баги - следствие использования модификатора. Или пишут настолько пространно, туманно и запутанно, что начинаешь сомневаться в их компетентности.

Поэтому считаю нужным выслушать начальников транспортн... противников volatile, хотя бы заочно. Вдруг что-то понятное и внятное скажут. Хотелось бы простыми человеческими словами (как можно более кратко) услышать - чем (и где, в каких случаях) я рискую используя этот модификатор?
Астролябия-сама меряет, было бы что мерять!!!
Реклама
Эиком - электронные компоненты и радиодетали
Модератор
Аватара пользователя
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

Asmodey писал(а):Хотелось бы простыми человеческими словами (как можно более кратко) услышать - чем (и где, в каких случаях) я рискую используя этот модификатор?
Ничем, кроме связыванием рук оптимизатору.
Зачем выдумывать какие-то ситуации и описывать их, если просто достаточно вникнуть в суть квалификатора и понять, что он делает ? Тогда все вопросы отпадут сами собой.
Реклама
Друг Кота
Аватара пользователя
Сообщения: 6823
Зарегистрирован: Сб янв 28, 2006 22:47:24

Сообщение Asmodey »

[uquote="Аlex",url="/forum/viewtopic.php?p=4162174#p4162174"]Зачем выдумывать какие-то ситуации и описывать их, если просто достаточно вникнуть в суть квалификатора и понять, что он делает ? Тогда все вопросы отпадут сами собой.[/uquote]Я знаю что он делает. Не знаю почему его считают опасным. Мало ли, может пропустил чего.

[uquote="Аlex",url="/forum/viewtopic.php?p=4162174#p4162174"]Ничем, кроме связыванием рук оптимизатору.[/uquote]
Понятно. А связывание, это хорошо. Потому как даже с отключенной оптимизацией компиляторы все равно продолжают чего-то там оптимизировать, причем подло - втихую и без предупреждения.
Астролябия-сама меряет, было бы что мерять!!!
Реклама
Мудрый кот
Сообщения: 1803
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

[uquote="SfS",url="/forum/viewtopic.php?p=4162026#p4162026"]

Код: Выделить всё

unsigned char* gpio = (unsigned char*)0x0507; 
while( (*gpio) & (1<<3) ) {} // Ждём нажатия кнопки
Тут сложно точно сказать, что произойдёт. Но многие компиляторы соптимизируют этот цикл до однократного чтения из регистра gpio и затем уйдёт в бесконечный цикл, если в момент опроса кнопка не нажата.[/uquote]А самые умные, если увидят, что незадолго до этого цикла, была запись по этому адресу какого-то значения X, могут и "однократное чтение" удалить как ненужное, использовав сохранённое в регистре значение X. :)

А ведь (раз это адрес регистра периферии) по данному адресу для операций чтения и для операций записи могли находиться даже совершенно разные регистры.
SfS
Друг Кота
Сообщения: 19756
Зарегистрирован: Пт янв 12, 2007 11:21:39
Откуда: Томск

Сообщение SfS »

[uquote="Asmodey",url="/forum/viewtopic.php?p=4162066#p4162066"]Ведь более опытные товарищи то тут, то там пишут - volatile опасносте. Но, редко пишут в чем же проявляются баги - следствие использования модификатора. Или пишут настолько пространно, туманно и запутанно, что начинаешь сомневаться в их компетентности.

Поэтому считаю нужным выслушать начальников транспортн... противников volatile, хотя бы заочно. Вдруг что-то понятное и внятное скажут. Хотелось бы простыми человеческими словами (как можно более кратко) услышать - чем (и где, в каких случаях) я рискую используя этот модификатор?[/uquote]

Никакой опасности от volatile нет.

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

Если написано, что надо считать значение волатильной переменной, то программа обязательно произведет чтение ячейки памяти.

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

Это чуть чуть увеличивает размер кода и немного замедляет программу. Но делает ее более предсказуемой.

Разумеется, что для обычных переменных это, как правило, не нужно.
Собутыльник Кота
Аватара пользователя
Сообщения: 2516
Зарегистрирован: Пт июл 12, 2019 22:52:01

Сообщение Eddy_Em »

[uquote="SfS",url="/forum/viewtopic.php?p=4162036#p4162036"]Оптимизацию полностью отключить:)[/uquote]
Жееесть!
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда.
Я на гитхабе, в ЖЖ
Контактная информация:
SfS
Друг Кота
Сообщения: 19756
Зарегистрирован: Пт янв 12, 2007 11:21:39
Откуда: Томск

Сообщение SfS »

Кстати, есть возможность заставить известные компиляторы С и без волатайла всегда читать и писать ячейку памяти. Догадайтесь, какие:) только выигрыша это не дает:)
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

SfS писал(а):Никакой опасности от volatile нет
всегда найдется веревка достаточной длины... ну. вы понели :)))

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


хотя бед от "неприменения" volatile, по моему мнению, все-таки больше :)))
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Модератор
Аватара пользователя
Сообщения: 19093
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Сообщение Starichok51 »

ARV писал(а):если чтение регистра что-то там сбрасывает в другом регистре
пример такого идиотизма в студию!
каким образом чтение регистра может повлиять на другой регистр???
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Starichok51 писал(а):пример такого идиотизма в студию!
легко: смотрим на регистр статуса USART AVR:
Bit 7 - RXC: UART Receive Complete - Прием завершен
Данный бит устанавливается в состояние 1 при пересылке принятого символа из сдвигового регистра приема в UDR. Бит устанавливается вне зависимости от отсутствия или наличия ошибок приема кадра. При установленном в UCR бите RXCIE и установленном бите RXC выполняется прерывание по завершению приема UART. Бит RXC очищается при считывании UDR. При приеме данных инициированном прерыванием, подпрограмма обработки прерывания по завершению приема UART должна считать UDR, с тем, чтобы очистить RXC, иначе по окончании подпрограммы обработки прерывания произойдет новое прерывание.
как видите, читаем регистр данных, бит падает в регистре статуса.

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

Мой уютный бложик... заходите!
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="ARV",url="/forum/viewtopic.php?p=4163165#p4163165"]как видите, читаем регистр данных, бит падает в регистре статуса.[/uquote]
И из-за этого получаем трудно вылавливаемый баг? А если бы не было volatile и компилятор мог читать или не читать из регистра когда ему вздумается, соответственно флаг мог сбрасываться или нет, то никаких проблем? :)
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

Мой уютный бложик... заходите!
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

Правильно, этот пример демонстрирует полезность volatile, а не его потенциальную опасность.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

предположим, что чтение регистра REG вызывает прерывание (потому что где в другом регистре битик устанавливается)

Код: Выделить всё

REG; REG;
в случае не-volatie прерывания не вызовет, с volatile - аж два раза
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="ARV",url="/forum/viewtopic.php?p=4163194#p4163194"]в случае не-volatie прерывания не вызовет, с volatile - аж два раза[/uquote]
Нет, в случае с не-volatile получим два, одно или ни одного прерывания. А два, или прерывание при каждом чтении, - это как раз правильное число прерываний и есть.
Последний раз редактировалось Reflector Пн янв 17, 2022 21:41:13, всего редактировалось 1 раз.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

Мой уютный бложик... заходите!
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

Что тем хуже? Программист читая регистр знает, что должно генериться прерывание, возможно именно для этого он его и читает, а без volatile результат непредсказуем...
Ответить

Вернуться в «Разные вопросы по МК»