|
|
  |
Вопрос по volatile |
|
|
|
Jan 18 2017, 14:06
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(Alt.F4 @ Jan 18 2017, 14:33)  Ну как бы изменения делаем атомарно, это уже обсудили. Ну как бы вам уже несколько человек на пальцах объяснили, что атомарность тут не причем  Попробую еще раз: Если глобальная переменная не объявлена volatile, то ваша функция из главного цикла, работая с этой переменной, имеет право (но не обязана) поместить её копию в регистр или в стек. И внутри функции работать с этой копией (атомарно или как хочет) согласно вашей программы. И только при выходе из функции записать эту копию в основное глобальное место. Т.е. во время работы функции могут быть две копии (и различные!). Прерывания этого не знают и работают с переменной из глобальной памяти. И функция при выходе результат работы прерывания затирает. А если объявить эту глобальную переменную volatile, то компилятору будет запрещено делать копии и работать с ними. Все изменения этой переменной будут происходить только по основному месту хранения.
|
|
|
|
|
Jan 18 2017, 15:08
|
Профессионал
    
Группа: Свой
Сообщений: 1 214
Регистрация: 23-12-04
Пользователь №: 1 643

|
Приветствую! Цитата(Baser @ Jan 18 2017, 17:06)  Ну как бы вам уже несколько человек на пальцах объяснили, что атомарность тут не причем  ... Если глобальная переменная не объявлена volatile, то ваша функция из главного цикла, работая с этой переменной, имеет право (но не обязана) поместить её копию в регистр или в стек. И внутри функции работать с этой копией (атомарно или как хочет) согласно вашей программы. И только при выходе из функции записать эту копию в основное глобальное место. Т.е. во время работы функции могут быть две копии (и различные!). Прерывания этого не знают и работают с переменной из глобальной памяти. И функция при выходе результат работы прерывания затирает. А если объявить эту глобальную переменную volatile, то компилятору будет запрещено делать копии и работать с ними. Все изменения этой переменной будут происходить только по основному месту хранения. Простите но это фигня "... а не заливная рыба ..."  Вы тоже путаете атомарность и Volatile. Глобальная переменная может быт считана в регистр для работы с ней, но копироваться при этом в стек она не будет. Еще раз "Volatile указывает компилятору что значение переменной может изменится ВНЕ текущего контекста зоны видимости переменной" Другими словами - меняет правило расчета "срока жизни" переменной в текущем контексте кода. Это значит что если в текущем контексте функции/таска/isr требуется несколько чтений из переменной то ВСЕ они БУДУТ РЕАЛЬНО выполнены из актуального местоположения переменной. И не важно - будет ли изменятся или нет переменная этом контексте. Например если var не volatile и Вы хотите: Код sum += var; sum += var; компилятор может сделать одно чтение var затем умножение на 2 и сложение с sum так как срок жизни переменной не заканчивается если будет volatile - то ВСЕГДА будет 2 чтения из памяти. Вот еще пример - иногда требуется только сам факт чтения/записи переменной (без реальных значений) для выполнения какого либо действия в периферии. Без volatile такое "бессмысленное расточительство" режется на корню "слишком умным" компилятором. Удачи! Rob.
|
|
|
|
|
Jan 18 2017, 16:14
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-07-16
Пользователь №: 92 484

|
Цитата(jcxz @ Jan 16 2017, 14:19)  Атомарность тут не при чём. Компилятор может например перенести значение переменной в регистр. И будете Вы в неё писать хоть атомарно, хоть нет, обработчик прерывания этого не знает когда захочет её прочитать из памяти где осталось старое значение. Если переменная не объявлена как регистЕр, то никуда компилятор её не перенесёт. Не вводите людей в заблуждение. А если я работаю с переменной через указатель на неё, то что? Я должен такую переменную объявлять как волатайл? Цитата(gerber @ Jan 16 2017, 17:55)  Компилятор (и примкнувший к нему линкер) вообще ничего не знают про прерывания. Есть только функции, расположенные по определенным адресам. А то, что эти функции могут обрабатывать прерывания, и прерывать другие функции в любом месте, знает только разработчик на основе архитектуры контроллера, под который он пишет программу. А в вашем примере и volatile не спасет, так как суммирование x и чисел N1,N2,N3 неатомарно. Есть такая штука, называется "сохранение контекста прерванной задачи". Хотя конечно есть двухбайтовые команды работы напрямую с РВВ, обращение к которым должно происходить в соседних тактах процессора. Тогда ой. Нужно запрещать прерывания перед такой критической последовательностью команд. Иначе вообше может произойти катастрофа если прерывание разорвет двухбайтовую последовательность команд Цитата(Alt.F4 @ Jan 17 2017, 22:45)  jcxz, вот именное такие: "например можете в цикле ждать когда установится некий флажок" (цитирую Вас же).
RobFPGA, по идее вообще без разницы, где эта переменная будет изменяться, что-то мы ходим по кругу...
Проблема: компилятор оптимизируя вырезает кусок кода считая его лишним. На основании чего он это делает? Он знает, что некая переменная имеет в текущий момент определенное состояние. Значит, если на входе функции мы не присваиваем ей какое либо значение и затем не проверяем эту переменную на другие значения (что для компилятора кажется безумным и он это режет), то и volatile не нужен. Именно так. Именно для этого введен волатйл. Чтобы запретить компилятору "оптимизировать" (чаще всего просто удалять) куски кода, которые содержат якобы не изменяемые и не используемые переменные
|
|
|
|
|
Jan 18 2017, 16:38
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(RobFPGA @ Jan 18 2017, 17:08)  Простите но это фигня "... а не заливная рыба ..."  Вы тоже путаете атомарность и Volatile. Ничего не имею против ваших разъяснений работы volatile, но доводов того, что "моя заливная рыба" это фигня, я у вас не нашел. Атомарность это запрещение различных прерываний, исключений, переключения задач и т.д. на каком-то участке кода. Это никак не запрещает компилятору в функции работать с переменной атомарно, но при этом работать с её копией. Цитата Глобальная переменная может быт считана в регистр для работы с ней, но копироваться при этом в стек она не будет. Для простых систем, где память переменных и стек находятся в равнозначной памяти - не будет. Но бывают и другие виды памяти, доступ к которым различен по времени. Вот в этом случае такое вполне возможно. Цитата(DASM @ Jan 18 2017, 16:32)  Господа никогда не понимал таких споров. Процесс важен или результат? Для достижения желаемого результата неплохо бы разбираться в особенностях процесса
|
|
|
|
|
Jan 18 2017, 17:06
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Укушенный воблой @ Jan 18 2017, 18:14)  Если переменная не объявлена как регистЕр, то никуда компилятор её не перенесёт. Вы, как обычно, совершенно ничего не смыслите в том, о чем пытаетесь писать. Разумеется компилятор может и делает это. Да и квалификатор register уже давно у большинства компиляторов просто игнорируется. Цитата Не вводите людей в заблуждение. Это по Вас. Цитата Именно для этого введен волатйл. Чтобы запретить компилятору "оптимизировать" (чаще всего просто удалять) куски кода, которые содержат якобы не изменяемые и не используемые переменные Опять ерунда - оптимизировать работу с переменной volatile не запрещает. Оптимизация, неизменяемость и неиспользование это три разных понятия. Кроме них еще есть одно важное понятие - обращение к переменной. Цитата(Baser @ Jan 18 2017, 18:38)  Атомарность это запрещение различных прерываний, исключений, переключения задач и т.д. на каком-то участке кода. Никак нет. То, что описали, это критическая секция, точнее один из вариантов ее реализации. Соответственно и все, что дальше написали это не о чем  Цитата(Baser @ Jan 18 2017, 16:06)  А если объявить эту глобальную переменную volatile, то компилятору будет запрещено делать копии и работать с ними. Все изменения этой переменной будут происходить только по основному месту хранения. Расскажите это создателям ARM ядра и множества других с Load-Store  . Они просто не умеют, так, как Вы представляете.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 18 2017, 18:09
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(zltigo @ Jan 18 2017, 19:06)  Никак нет. То, что описали, это критическая секция, точнее один из вариантов ее реализации. Соответственно и все, что дальше написали это не о чем  Это самая обыкновенная программная атомарность. Если к защищаемым переменным в системе нет аппаратного доступа других устройств, то программная атомарность работает аналогично аппаратной. Цитата(zltigo @ Jan 18 2017, 19:06)  Расскажите это создателям ARM ядра и множества других с Load-Store  . Они просто не умеют, так, как Вы представляете. Наверно, здесь был не точен. Конечно, volatile не создаст атомарный доступ к переменной, но хотя бы предотвратит создание компилятором её "долгоживущих" копий.
|
|
|
|
|
Jan 18 2017, 22:36
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(Укушенный воблой @ Jan 18 2017, 20:31)  А если я работаю с переменной через указатель на неё, то что? Я должен такую переменную объявлять как волатайл? Правила те же: указатель должен быть объявлен как указатель на volatile-переменную. Естественно компилятор сгенерирует соответствующий код: перед каждым использованием переменной он будет считывать ее значение заново. Цитата(DASM @ Jan 18 2017, 16:32)  Господа никогда не понимал таких споров. Процесс важен или результат? Сюрпризы возможны, не проще ли дать volatile и забыть? "Споры" нужны для того, чтобы понять при каких ситуациях "сюрпризы возможны".
|
|
|
|
|
Jan 19 2017, 04:18
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-07-16
Пользователь №: 92 484

|
Цитата(aiwa @ Jan 18 2017, 22:36)  Правила те же: указатель должен быть объявлен как указатель на volatile-переменную. А сама переменная? Сама переменная должна объявляться как волатАЙл если её используют через указатель? Что-то то я не видел в реальном коде разных разработчиков этого Причем используют неявно. Без использования операции "взятие адреса" (амперсанд)
|
|
|
|
|
Jan 19 2017, 06:34
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(Укушенный воблой @ Jan 19 2017, 07:18)  А сама переменная? Сама переменная должна объявляться как волатАЙл если её используют через указатель? Нет. Компилятор вещь достаточно умная - он понимает что такое указатель и к чему может привести взятие адреса переменной (для этой переменной) Он даже понимает кое что в том, куда этот указатель может указывать - см ключевое слово restrict и опцию ansi alias ( http://www.atmos.washington.edu/~ovens/ifo...nsi_alias_f.htm )
|
|
|
|
|
Jan 19 2017, 06:54
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
jcxz, а мне Ваши примеры напоминают троллинг, повторение описанного ранее. Я не вижу причин для сбоя в Вашем коде. В описанной ситуации функция не увидит возведение флажка, при втором входе в функцию все будет Ок. Повторюсь: где проблема? Да и пример я бы назвал "странным" (читай так делать нельзя), т.к. оба прерывания используют один и тот же байт-флаг и если они сработают друг за другом, то Вы не будете знать, что сработало первое. Цитата Если записи нет, а есть только чтения, то компилятор заменит все чтения этой переменной загрузкой константы в регистр. А потом компоновщик увидит, что к этой переменной нет ни одного обращения и удалит её за ненадобностью. Какой еще константы, если она переменная? Как это нет обращения, Вы же ее читаете. Цитата И функция при выходе результат работы прерывания затирает. Если переменная изменяется в функции, то это делать надо атомарно, и тогда не будет описанной Вами проблемы. Если что, то volatile не спасет от написанного Вами. Цитата Вот еще пример - иногда требуется только сам факт чтения/записи переменной (без реальных значений) для выполнения какого либо действия в периферии. Без volatile такое "бессмысленное расточительство" режется на корню "слишком умным" компилятором. Хороший пример, добавляем в копилку. Хотя можно объявить переменную глобально или extern, тоже будет работать. Цитата "Споры" нужны для того, чтобы понять при каких ситуациях "сюрпризы возможны". Пока имеем следующие ситуации требущие объявления volatile: 1) Переменные в блокирующих операций (ожидание определенного значения в цикле) 2) Все системные регистры, в том числе ввода/вывода (хотя они де-факто volatile) 3) Переменные в которые "просто читаем" из какого-либо регистра (как вариант, используем глобальную или extern переменную)Еще пару примеров и можно делать мини-FAQ по теме вопроса
|
|
|
|
|
Jan 19 2017, 07:11
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (Alt.F4 @ Jan 19 2017, 08:54)  Пока имеем следующие ситуации требущие объявления volatile: Не надо изобретать велосипед. Есть два критения: 1) если переменная используется в двух потоках (читай - в основном цикле и прерывании). Компилятор обязан при каждом обращении на чтение вычитывать ее из ОЗУ и при каждом обращении на запись класть обратно. 2) если важен порядок доступа к этой переменной (читай - регистры периферии). Компилятор обязан делать то же самое, что и п.1, кроме этого не имеет права менять местами обращения к этой переменной относительно обращений к другим volatile. QUOTE (Укушенный воблой @ Jan 19 2017, 06:18)  Сама переменная должна объявляться как волатАЙл если её используют через указатель? Какая разница, как к ней обращаются? Если она попадает под указзанные выше два критерия - должна быть volatile. Все. И указатель должен иметь тип "указатель на volatile". Зачем так сделано - подумайте самостоятельно.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 19 2017, 07:18
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Alt.F4 @ Jan 19 2017, 09:54)  Да и пример я бы назвал "странным" (читай так делать нельзя), т.к. оба прерывания используют один и тот же байт-флаг и если они сработают друг за другом, то Вы не будете знать, что сработало первое. Специально для Вас разжёвываю: функционал isr1 и isr2 взаимосвязан - если произошло одно, то выставление второго не нужно и даже запрещено (нужно знать какое было первое). И процедура сервиса, будучи запущенной, проверит и обслужит события. Если совсем уже до детского уровня разжевать, например: это прерывания от двух счётчиков, запущенных от одного события, считающих с разной частотой, кто досчитает первым - тот и должен послать Int. Процедуре сервиса важно знать кто был первый, но обработка - общая.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|