Зачем volatile
нужен в С? Для чего его используют? Что это будет делать?
c
declaration
volatile
Джонатан Леффлер
источник
источник
Ответы:
Volatile говорит компилятору не оптимизировать ничего, что связано с переменной volatile.
Существует как минимум три распространенных причины его использования, все из которых включают ситуации, в которых значение переменной может изменяться без действия видимого кода: когда вы взаимодействуете с оборудованием, которое изменяет само значение; когда работает другой поток, который также использует переменную; или когда есть обработчик сигнала, который может изменить значение переменной.
Допустим, у вас есть небольшой аппаратный блок, который где-то отображается в ОЗУ и имеет два адреса: порт команды и порт данных:
Теперь вы хотите отправить команду:
Выглядит просто, но может не получиться, потому что компилятор может свободно изменять порядок записи данных и команд. Это заставит наш маленький гаджет выдавать команды с предыдущим значением данных. Также взгляните на ожидание при занятой петле. Этот будет оптимизирован. Компилятор попытается быть умным, прочитает значение isbusy только один раз и затем перейдет в бесконечный цикл. Это не то, что вы хотите.
Чтобы обойти это, нужно объявить гаджет-указатель как volatile. Таким образом, компилятор вынужден делать то, что вы написали. Он не может удалить назначения памяти, он не может кэшировать переменные в регистрах и не может изменить порядок назначений:
Это правильная версия:
источник
volatile
в C фактически возникла с целью не кэшировать значения переменной автоматически. Он скажет компилятору не кэшировать значение этой переменной. Таким образом, он будет генерировать код для получения значения даннойvolatile
переменной из основной памяти каждый раз, когда встречается с ней. Этот механизм используется потому, что в любое время значение может быть изменено ОС или любым прерыванием. Таким образом, использованиеvolatile
поможет нам получить доступ к значению заново каждый раз.источник
volatile
состояла в том, чтобы позволить компиляторам оптимизировать код, в то же время позволяя программистам достигать семантики, которая была бы достигнута без такой оптимизации. Авторы Стандарта ожидали, что качественные реализации будут поддерживать любую семантику, полезную с учетом их целевых платформ и областей применения, и не ожидали, что разработчики компиляторов будут стремиться предлагать семантику самого низкого качества, которая соответствует Стандарту и не была на 100%. глупо (обратите внимание, что авторы Стандарта прямо признают это в обосновании ...Другое использование для
volatile
это обработчики сигналов. Если у вас есть такой код:Компилятору разрешено замечать, что тело цикла не касается
quit
переменной, и преобразовывать цикл вwhile (true)
цикл. Даже еслиquit
переменная установлена в обработчике сигнала дляSIGINT
иSIGTERM
; у компилятора нет возможности узнать это.Однако, если
quit
переменная объявленаvolatile
, компилятор вынужден загружать ее каждый раз, потому что она может быть изменена в другом месте. Это именно то, что вы хотите в этой ситуации.источник
quit
, компилятор может оптимизировать его в постоянный цикл, предполагая, что что нет способаquit
измениться между итерациями. NB: Это не обязательно хорошая замена для реального программирования с защитой потоков.volatile
или других маркеров предполагается, что ничто вне цикла не изменяет эту переменную, как только она входит в цикл, даже если это глобальная переменная.extern int global; void fn(void) { while (global != 0) { } }
сgcc -O3 -S
и посмотреть на полученный файл сборки, на моей машине это делаетmovl global(%rip), %eax
;testl %eax, %eax
;je .L1
;.L4: jmp .L4
то есть бесконечный цикл, если глобальное не ноль. Затем попробуйте добавитьvolatile
и увидеть разницу.volatile
сообщает компилятору, что ваша переменная может быть изменена другими способами, а не кодом, который обращается к ней. например, это может быть область памяти, отображаемая I / O. Если это не указано в таких случаях, некоторые переменные доступы могут быть оптимизированы, например, их содержимое может храниться в регистре, и ячейка памяти не будет считываться снова.источник
Смотрите эту статью Андрея Александреску, " volatile - лучший друг многопоточного программиста "
Статья относится как к, так
C
и кC++
.Также см. Статью « С ++ и опасности двойной проверки блокировки » Скотта Мейерса и Андрея Александреску:
источник
volatile
не гарантирует атомарность.Мое простое объяснение:
В некоторых сценариях, основанных на логике или коде, компилятор выполняет оптимизацию переменных, которые, по его мнению, не изменяются. В
volatile
предотвращает ключевое слово переменной оптимизируется.Например:
Исходя из приведенного выше кода, компилятор может считать, что
usb_interface_flag
он определен как 0, и что в цикле while он всегда будет равен нулю. После оптимизации компилятор будет обрабатывать его какwhile(true)
постоянно, что приведет к бесконечному циклу.Чтобы избежать подобных сценариев, мы объявляем флаг как volatile, мы сообщаем компилятору, что это значение может быть изменено внешним интерфейсом или другим модулем программы, т. Е. Пожалуйста, не оптимизируйте его. Это случай использования для летучих.
источник
Предельное использование для volatile следующее. Допустим, вы хотите вычислить числовую производную функции
f
:Проблема в том, что,
x+h-x
как правило, не равноh
из-за ошибок округления. Подумайте об этом: когда вы вычитаете очень близкие числа, вы теряете много значащих цифр, которые могут испортить вычисление производной (подумайте 1.00001 - 1). Возможный обходной путь может бытьно в зависимости от вашей платформы и переключателей компилятора вторая строка этой функции может быть уничтожена агрессивно оптимизирующим компилятором. Так ты пишешь вместо
заставить компилятор прочитать ячейку памяти, содержащую hh, исключая возможную возможность оптимизации.
источник
h
илиhh
в производной формуле? Когдаhh
вычисляется последняя формула использует его, как первая, без разницы. Может быть так и должно быть(f(x+h) - f(x))/hh
?h
иhh
заключается в томhh
, что операция усекается до некоторой отрицательной степени двухx + h - x
. В этом случаеx + hh
иx
отличаются ровно наhh
. Вы также можете взять свою формулу, она даст тот же результат, так какx + h
иx + hh
равны (это знаменатель, который здесь важен).x1=x+h; d = (f(x1)-f(x))/(x1-x)
? без использования летучих.-ffast-math
или эквивалентно.Есть два использования. Они особенно часто используются во встроенных разработках.
Компилятор не будет оптимизировать функции, которые используют переменные, которые определены с ключевым словом volatile
Volatile используется для доступа к точным ячейкам памяти в ОЗУ, ПЗУ и т. Д. Это чаще используется для управления отображаемыми в памяти устройствами, доступа к регистрам ЦП и определения местоположения определенных областей памяти.
Смотрите примеры со списком сборок. Re: использование C "volatile" ключевое слово в разработке встраиваемых
источник
Volatile также полезно, когда вы хотите заставить компилятор не оптимизировать конкретную последовательность кода (например, для написания микропроцессора).
источник
Я упомяну другой сценарий, где летучие вещества важны.
Предположим, что вы отображаете в памяти файл для более быстрого ввода-вывода, и этот файл может измениться за кулисами (например, файл не находится на вашем локальном жестком диске, а вместо этого обслуживается по сети другим компьютером).
Если вы обращаетесь к данным файла, отображенного в памяти, через указатели на энергонезависимые объекты (на уровне исходного кода), то код, сгенерированный компилятором, может извлекать одни и те же данные несколько раз, даже не зная об этом.
Если эти данные изменятся, ваша программа может использовать две или более разных версий данных и перейти в несогласованное состояние. Это может привести не только к логически некорректному поведению программы, но и к уязвимым местам в ней, если она обрабатывает ненадежные файлы или файлы из ненадежных расположений.
Если вы заботитесь о безопасности, и вам следует, это важный сценарий для рассмотрения.
источник
volatile означает, что хранилище может измениться в любое время и измениться, но что-то вне контроля пользовательской программы. Это означает, что если вы ссылаетесь на переменную, программа должна всегда проверять физический адрес (то есть отображенный ввод fifo), а не использовать его в кэшированном виде.
источник
Вики говорят все о
volatile
:И документ о ядре Linux также делает отличную запись о
volatile
:источник
На мой взгляд, не стоит ожидать слишком многого от
volatile
. Чтобы проиллюстрировать это, посмотрите на пример из высоко оцененного ответа Нильса Пипенбринка .Я бы сказал, его пример не подходит для
volatile
.volatile
используется только для: предотвращения компиляции полезных и желательных оптимизаций . Речь не идет о потоке безопасном, атомарном доступе или даже порядке памяти.В этом примере:
gadget->data = data
, преждеgadget->command = command
всего гарантируется только в скомпилированный код с помощью компилятора. Во время выполнения процессор все еще может переупорядочивать данные и назначение команд в соответствии с архитектурой процессора. Оборудование может получить неправильные данные (предположим, гаджет сопоставлен с аппаратным вводом / выводом). Необходим барьер памяти между данными и назначением команд.источник
volatile
это ухудшает производительность без всякой причины. Что касается того, достаточно ли это, это будет зависеть от других аспектов системы, о которых программист может знать больше, чем компилятор. С другой стороны, если процессор гарантирует, что инструкция для записи по определенному адресу очистит кэш-память ЦП, но компилятор не предоставил способа очистить переменные с кэшем регистров, о которых ЦП ничего не знает, очистка кеша была бы бесполезной.В языке, разработанном Деннисом Ритчи, каждый доступ к любому объекту, кроме автоматических объектов, адрес которых не был взят, будет вести себя так, как если бы он вычислял адрес объекта, а затем считывал или записывал хранилище по этому адресу. Это сделало язык очень мощным, но сильно ограничило возможности оптимизации.
Хотя можно было бы добавить квалификатор, который бы пригласил компилятор предположить, что конкретный объект не будет изменен странным образом, такое предположение было бы уместно для подавляющего большинства объектов в программах на C, и это имело бы было непрактично добавлять классификатор ко всем объектам, для которых было бы целесообразно такое предположение. С другой стороны, некоторые программы должны использовать некоторые объекты, для которых такое предположение не будет выполнено. Чтобы решить эту проблему, Стандарт говорит, что компиляторы могут предполагать, что объекты, которые не объявлены
volatile
, их значение не будет наблюдаться или изменяться способами, которые находятся вне контроля компилятора или будут находиться за пределами разумного понимания компилятора.Поскольку различные платформы могут иметь разные способы наблюдения или изменения объектов за пределами контроля компилятора, целесообразно, чтобы качественные компиляторы для этих платформ отличались точной обработкой
volatile
семантики. К сожалению, поскольку в стандарте не было предложено, чтобы качественные компиляторы, предназначенные для низкоуровневого программирования на платформе, обрабатывалиvolatile
таким образом, чтобы распознавать любые и все соответствующие эффекты конкретной операции чтения / записи на этой платформе, многие компиляторы не справляются с этой задачей. таким образом, это затрудняет обработку таких вещей, как фоновый ввод-вывод, способом, который эффективен, но не может быть нарушен «оптимизацией» компилятора.источник
Проще говоря, он говорит компилятору не выполнять какую-либо оптимизацию для конкретной переменной. Переменные, которые отображаются в регистр устройства, изменяются устройством косвенно. В этом случае необходимо использовать энергозависимые.
источник
Volatile может быть изменено извне скомпилированного кода (например, программа может отобразить переменную volatile в регистр с отображением в памяти.) Компилятор не будет применять определенные оптимизации к коду, который обрабатывает переменную volatile - например, он выиграл ' загрузить его в регистр без записи в память. Это важно при работе с аппаратными регистрами.
источник
Как справедливо предлагают многие здесь, популярное использование ключевого слова volatile - пропустить оптимизацию переменной volatile.
Самое лучшее преимущество, которое приходит на ум и стоит упомянуть после прочтения о volatile - это предотвращение отката переменной в случае a
longjmp
. Нелокальный прыжок.Что это значит?
Это просто означает, что последнее значение будет сохранено после разматывания стека , чтобы вернуться к некоторому предыдущему кадру стека; как правило, в случае какого-либо ошибочного сценария.
Поскольку это выходит за рамки этого вопроса, я не буду вдаваться в подробности
setjmp/longjmp
, но о нем стоит прочитать; и как функция волатильности может использоваться для сохранения последнего значения.источник
это не позволяет компилятору автоматически изменять значения переменных. переменная переменная предназначена для динамического использования.
источник