|
|
  |
Вопрос по volatile |
|
|
|
Jan 16 2017, 15:41
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(SlavaV @ Jan 16 2017, 18:14)  Переменная не "путешествует" во время выполнения из памяти в регистр или обратно, если компилятор определил ей место в памяти или в регистре то она там и будет, но если ей определенно место в регистре то компилятор код добавит при входе в ISR "push" при выходе "pop" и то при условии если этот регистр используется во время обработки прерывания.
PS может я чего не знаю, но почему в процедуре обработки прерывания нельзя обращаться к переменным расположенным в регистре? Каким образом обращаться-то??? К переменной, расположенной в регистре в фоновой задаче и выгруженной куда-то в стек при активации прерывания. Переменная именно что "путешествует". Она может находиться в памяти, а через такт - уже в регистре, а потом опять в памяти. Это и есть работа оптимизатора. Пример: int x; void f() { x += N1; //1 ... x += N2; //2 ... x += N3; //3 } В точке 1 компилятор может загрузить x в какой-либо регистр и все обращения внутри f() к этой переменной выполнять с этим с регистром, и выгрузить её в ОЗУ только перед выходом из функции. Или не перед выходом, а на любом участке выполнения, где сочтёт это оптимальным. Это вполне допустимое поведение. А теперь представьте что будет, если где-то во время выполнения f() вызвался ISR, который обратился к переменной x.
|
|
|
|
|
Jan 16 2017, 16:44
|

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

|
QUOTE (SlavaV @ Jan 16 2017, 17:55)  вы считаете разработчиков компилятора недалёкими людьми которые не предполагают, что прерывание может возникнуть в любой момент времени? Нет - мы знаем, что они умные люди и понимают, что программист хочен получить малнькую и быструю программу, а не большую и медленную. Также они понимают, что только программист знает, что должна делать его программа. Поэтому они придумали volatile. QUOTE (SlavaV @ Jan 16 2017, 17:55)  PS описанное вами поведение переменной ни на практике ни в теории я не видел ни разу. Или вы мало видели, или никогда не включали оптимизацию.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 16 2017, 16:55
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(SlavaV @ Jan 16 2017, 18:55)  вы считаете разработчиков компилятора недалёкими людьми которые не предполагают, что прерывание может возникнуть в любой момент времени? Компилятор (и примкнувший к нему линкер) вообще ничего не знают про прерывания. Есть только функции, расположенные по определенным адресам. А то, что эти функции могут обрабатывать прерывания, и прерывать другие функции в любом месте, знает только разработчик на основе архитектуры контроллера, под который он пишет программу. А в вашем примере и volatile не спасет, так как суммирование x и чисел N1,N2,N3 неатомарно.
Сообщение отредактировал gerber - Jan 16 2017, 17:04
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Jan 16 2017, 17:40
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(gerber @ Jan 16 2017, 19:55)  А в вашем примере и volatile не спасет, так как суммирование x и чисел N1,N2,N3 неатомарно. Атомарность - это совсем другой вопрос. Я же ничего не писал про тип доступа из ISR. ISR может просто доступ на чтение сделать, чему неатомарность никак не помешает. Цитата(Alt.F4 @ Jan 16 2017, 20:10)  Снова приведен пример, как и во всех статьях. В реальности такую ситуации с тупым инкрементом или другой подобной логикой не встретить, имхо. Думается мне, что проблема высосана из пальца и требует ну совсем исключительных условий для объявления volatile. "Тупой инкремент" там для примера. На его месте могут быть любые другие операции модификации x. Перемежающиеся другими операциями если Вы не поняли. Если Вы не встречали такую ситуацию в реале, то смею предположить, что вообще не занимаетесь программированием. Например: элементарный буфер данных, в который фоновая программа пишет данные, передвигая после каждой записи указатель позиции, а ISR, читает эту позицию и на основании её значения - данные из буфера. Вот записали Вы данные, передвинули позицию, дальше например можете в цикле ждать когда установится некий флажок (устанавливаемый в ISR и прописанный отдельно, с volatile), а ISR смотрит - позиция-то не изменилась, значит данных нет. А в это время новая позиция просто в регистре лежит. Так и будет фоновый процесс бесконечно ждать.
|
|
|
|
|
Jan 16 2017, 19:03
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Пример (https://gcc.godbolt.org/) Код (компилятор ARM gcc 5.4.1, опции -O3) Код int flag;
void isr() {flag=1;}
void wait() { flag = 0; while(!flag); } Предположим, что процедура isr вызывется из приерывания (по таймеру) Результат: Код isr(): mov r2, #1 ldr r3, .L2 str r2, [r3] bx lr .L2: .word .LANCHOR0 wait(): mov r2, #0 ldr r3, .L7 str r2, [r3] .L5: b .L5 .L7: .word .LANCHOR0 flag: Как видим wait заканчивается бесконечным циклом. Добавим volatile Код isr(): mov r2, #1 ldr r3, .L2 str r2, [r3] bx lr .L2: .word .LANCHOR0 wait(): mov r3, #0 ldr r2, .L9 str r3, [r2] .L5: ldr r3, [r2] cmp r3, #0 beq .L5 bx lr .L9: .word .LANCHOR0 flag: Бесконечный цикл исчез
|
|
|
|
|
Jan 18 2017, 10:53
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
jcxz, сейчас я тоже начну вас оскорблять  Изменять все переменные, используемые в прерывании, необходимо атомарно (или вы не в курсе?), и нам в принципе без разницы, куда компилятор запишет значение во время преобразований, хоть в регистр, хоть будет "держать в уме". Пока по прежнему нет реального примера, кроме блокирующих операций, где компилятор может сделать не так, как предполагалось. В статьях обычно об этом пишут как-то абстрактно, а никто детально не желает разобраться, как действительно все работает.
|
|
|
|
|
Jan 18 2017, 11:12
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Alt.F4 @ Jan 18 2017, 13:53)  jcxz, сейчас я тоже начну вас оскорблять  Изменять все переменные, используемые в прерывании, необходимо атомарно (если вы не в курсе), и нам в принципе без разницы, куда компилятор запишет значение во время преобразований, хоть в регистр, хоть будет "держать в уме". Да при чём тут атомарность-то??? Вы хотя-бы прочитали и поняли что я написал? Изменений переменной в фоновом программе может вообще не быть.
|
|
|
|
|
Jan 18 2017, 11:23
|
Профессионал
    
Группа: Свой
Сообщений: 1 214
Регистрация: 23-12-04
Пользователь №: 1 643

|
Приветствую! Цитата(Alt.F4 @ Jan 18 2017, 13:53)  Изменять все переменные, используемые в прерывании, необходимо атомарно (если вы не в курсе), и нам в принципе без разницы, куда компилятор запишет значение во время преобразований, хоть в регистр, хоть будет "держать в уме".
Пока по прежнему нет реального примера, кроме блокирующих операций, где компилятор может сделать не так, как предполагалось. В статьях обычно об этом пишут как-то абстрактно, а никто детально не желает разобраться, как действительно все работает. Volatile к атомарности никакого отношения не имеет! Volatile указывает компилятору что значение переменной может изменится ВНЕ текущего контекста зоны видимости переменной. Поэтому компилятор НЕ вправе оптимизировать ЧТЕНИЕ актуального состояния этой переменной. Пример Вам уже приводили - чтение переменной в цикле. И не только как блокировка, а например очень часто при опросе регистров состояния периферии, биты которых меняются аппаратно. Удачи! Rob.
|
|
|
|
|
Jan 18 2017, 11:24
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
jcxz, читаю "на входе в эту функцию, компилятор вполне может переместить значение переменной в регистр" И каким боком из-за этого переменная может не изменяться вообще? В текущий момент пусть функция не видит изменения, при следующем входе снова прочитает по адресу и все увидит. Где проблема?
RobFPGA, об этом речи и не идет, атомарность делает сам программист, конечно. Ок, добавляем еще одну ситуацию, кроме блокирующих - чтение регистров ввода/вывода, хотя они уже по умолчанию идут как volatile, и программисту об этом не надо заботиться.
|
|
|
|
|
Jan 18 2017, 11:49
|
Профессионал
    
Группа: Свой
Сообщений: 1 214
Регистрация: 23-12-04
Пользователь №: 1 643

|
Приветствую! Цитата(Alt.F4 @ Jan 18 2017, 14:24)  jcxz, читаю "на входе в эту функцию, компилятор вполне может переместить значение переменной в регистр" И каким боком из-за этого переменная может не изменяться вообще? В текущий момент пусть функция не видит изменения, при следующем входе снова прочитает по адресу и все увидит. Где проблема?
RobFPGA, об этом речи и не идет, атомарность делает сам программист, конечно. Ок, добавляем еще одну ситуацию, кроме блокирующих - чтение регистров ввода/вывода, хотя они уже по умолчанию идут как volatile, и программисту об этом не надо заботиться. Это если за Вас уже позаботились сделав правильный BSP, а если эта периферия сидит например во внешней FPGA которая отмаплена на память? И не только регистры, а например буфера данных которые пишутся через DMA, а проц их только читает для обработки. И что с оптимизирует компилятор в этом случае если НИГДЕ в Вашей программе нет ЗАПИСИ в этот буфер/переменную? Удачи! Rob.
|
|
|
|
|
Jan 18 2017, 11:52
|

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

|
Цитата(Alt.F4 @ Jan 18 2017, 13:24)  jcxz, читаю "на входе в эту функцию, компилятор вполне может переместить значение переменной в регистр" И каким боком из-за этого переменная может не изменяться вообще? В текущий момент пусть функция не видит изменения, при следующем входе снова прочитает по адресу и все увидит. Где проблема? Если ваше прерывание будет только читать эту временно перемещенную переменную, то ничего не произойдет, она прочтет новое значение позже. А вот если прерывание тоже изменяет это значение, то это значение пропадет, так как функция при выходе сохранит в эту переменную свое значение. А если глобально про volatile, то я не слишком часто его применяю, только там, где вышеприведенные эффекты могут иметь место или есть опасность выкидывания компилятором моего кода. Обычно анализирую возможность поведения каждой переменной, и если гиблые состояния видны, применяю. А если простая передача данных по флагам из/в прерывания, то и так все работает.
|
|
|
|
|
Jan 18 2017, 12:33
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
RobFPGA, думаю, если буфер объявлен глобально, то компилятор не вырежет код, даже если "НИГДЕ в Вашей программе нет ЗАПИСИ в этот буфер/переменную". Имхо, компилятор работает исключительно локально, относительно кода функции, и не проверяет, в каком месте и пишутся ли вообще данные в глобальные переменные. Цитата А вот если прерывание тоже изменяет это значение, то это значение пропадет, так как функция при выходе сохранит в эту переменную свое значение. Ну как бы изменения делаем атомарно, это уже обсудили.
|
|
|
|
|
Jan 18 2017, 13:54
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(Alt.F4 @ Jan 17 2017, 23:45)  RobFPGA, по идее вообще без разницы, где эта переменная будет изменяться, что-то мы ходим по кругу... Переменная не может где-то изменяться. Она находится всегда там, где определена изначально: глобальная - в памяти, регистровая - в регистре, временная в стеке. И только так. Никаких "путешествий" она не производит по стекам и регистрам она не производит: в стек и регистры компилятор записывает копии значения переменных. Компилятор производит компиляцию исходя из предположения по умолчанию, что копия значения переменной всегда тождественна оригинальному, и как раз служебное слово volatile говорит компилятору, что это не так и переменная может поменять свое значение в прерываниии.
Сообщение отредактировал aiwa - Jan 18 2017, 13:54
|
|
|
|
|
Jan 18 2017, 14:05
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Alt.F4 @ Jan 18 2017, 14:24)  И каким боком из-за этого переменная может не изменяться вообще? В текущий момент пусть функция не видит изменения, при следующем входе снова прочитает по адресу и все увидит. Где проблема? У меня возникает ощущение что Вы троллите всех тут.... Как можно не понимать очевидных вещей?? Пример (жизненный, написал специально для Вас): CODE enum {F_NONE, F_SLEEP_STATE, F_TIMER, F_SERVICE_REQUEST_1, F_SERVICE_REQUEST_2}; int flag = F_NONE; void f() { if (flag == F_NONE) { //ТОЧКА1 ENTR_CRT_SECTION(); //входим в критическую секцию (просто запрет прерывания) if (flag == F_NONE) { flag = F_SLEEP_STATE; EXIT_CRT_SECTION(); //покидаем критическую секцию Sleep(); } else EXIT_CRT_SECTION(); } int i = flag; flag = F_NONE; switch (flag) { case F_TIMER: return; case F_SERVICE_REQUEST_1: ... //обслуживание сервиса 1 break; case F_SERVICE_REQUEST_2: ... //обслуживание сервиса 2 break; } } void isr1() { PeripheralIntReqReset1(); if (flag == F_SLEEP_STATE) WakeUp(); else if (flag != F_NONE && flag != F_TIMER) return; flag = F_SERVICE_REQUEST_1; } void isr2() { PeripheralIntReqReset2(); if (flag == F_SLEEP_STATE) WakeUp(); else if (flag != F_NONE && flag != F_TIMER) return; flag = F_SERVICE_REQUEST_2; } void isrTimer() { if (flag == F_SLEEP_STATE) WakeUp(); else if (flag != F_NONE) return; flag = F_TIMER; } Теперь представьте что эта функция вызывается периодически (суперцикл или корпоративная многозадачность). Включена максимальная оптимизация и на входе в функцию f() при первом обращении компилятор перенёс переменную flag в регистр, а в месте ТОЧКА1 происходит какое-либо прерывание, которое пишет запрос обслуживания в flag. Так вот - при компиляции с макс. оптимизацией, этот код будет сбоить (терять запросы на обслуживание). Но если к определению flag добавить volatile, всё будет прекрасно работать с любым уровнем оптимизации. Чтобы пресечь возможные доводы типа "искусственный пример", допустим: isr1()/isr2() к примеру - это ISR-ы о готовности данных к чтению в FIFO-буфере некоей периферии, которая однократно выставляет флаг готовности при достижении некоего уровня заполнения FIFO. Эти данные надо считать. Причём считать до прихода след. данных чтобы избежать потерь. И можно 1000 подобных примеров придумать и с более сложными зависимостями. Цитата(Alt.F4 @ Jan 18 2017, 15:33)  думаю, если буфер объявлен глобально, то компилятор не вырежет код, даже если "НИГДЕ в Вашей программе нет ЗАПИСИ в этот буфер/переменную". Опять неправда. Если записи нет, а есть только чтения, то компилятор заменит все чтения этой переменной загрузкой константы в регистр. А потом компоновщик увидит, что к этой переменной нет ни одного обращения и удалит её за ненадобностью.
|
|
|
|
|
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. Процедуре сервиса важно знать кто был первый, но обработка - общая.
|
|
|
|
|
Jan 19 2017, 07:32
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
Цитата Очевидно что Вы и читать исходники не умеете. А Вы убеждать не умеете. Использование одного и того же байта-флага в разных прерываниях описали, хорошо, но конкретной причины для дальнейшего сбоя по прежнему нет. Цитата 1) если переменная используется в двух потоках (читай - в основном цикле и прерывании). Компилятор обязан при каждом обращении на чтение вычитывать ее из ОЗУ и при каждом обращении на запись класть обратно. По идее так должно быть, но не обязательно же? Просто скорость реакции уменьшится (обновленные данные внешней операцией будут получены при повторном чтении переменной). Я не против volatile, просто хочется понять, когда без него ну совсем не обойтись и код точно не будет работать.
|
|
|
|
|
Jan 19 2017, 07:37
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Укушенный воблой @ Jan 18 2017, 21:31)  А если я работаю с переменной через указатель на неё, то что? Я должен такую переменную объявлять как волатайл? Ведь компилятор не может проследить все случаи где я получаю доступ к переменной через указатель на неё Попробуйте как-нить написать: c = *p; ...//пара операций if (*p) ... увидите что при наличии оптимизации, компилятор часто не будет делать второе чтение из памяти, а использует копию переменной в регистре, ранее прочитанную. Он не знает и не должен знать, что Вы возможно в параллельной задаче ОС или ISR можете её изменить. Об этом должен думать программист. Тем более если такое написано в теле цикла - компилятор тем более *p считает в регистр и всё время цикла будет её там хранить. А вот уже если в этом теле цикла вы сделаете *p++, то тут поведение компилятора различно: если в этом цикле нет обращений через указатели к памяти, и стоит оптимизация "по скорости" то скорей всего он и модификацию выполнит в регистре, а сохранение в память - уже после выхода из цикла. Если же обращения есть, то может сделать и так и так - в зависимости от компилятора от степени оптимизации. К тому же в некоторых компиляторах для указателей можно задавать специальные модификаторы, говорящие что работа с данными через них не имеет "side effects", т.е. - что указатель указывает на свои отдельные данные, которые никак не пересекаются с данными других указателей и областями хранения переменных используемых в этой функции. Это позволяет компилятору выполнять оптимизацию работы с этими указателями по-максимуму, например: загрузка данных из области этих указателей может выполняться заранее (например в коде есть несколько обращений: p[1], p[2], p[3],... размазанных по циклу, компилятор может загрузить эти данные сразу в одном месте или даже вообще до входа в цикл или загружать данные для следующего прохода цикла в предыдущем проходе, менять кол-во проховдов цикла, частично или полностью разворачивать его, делая предварительную загрузку, а сохранение в такие указатели вообще - в последующих проходах цикла). Это позволяет конвееризировать обработку данных, что очень ярко видно на процессорах, умеющих распараллеливать вычисления в несколько потоков, имеющих несколько АЛУ, например - DSP. DSP-шные оптимизаторы выполняют как правило такое преобразование кода, которое и не снилось на ARM-системах. Так как там в разы больше регистров и способов адресации памяти.
|
|
|
|
|
Jan 19 2017, 17:24
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-07-16
Пользователь №: 92 484

|
Цитата(XVR @ Jan 19 2017, 06:34)  Нет. Компилятор вещь достаточно умная - он понимает что такое указатель и к чему может привести взятие адреса переменной (для этой переменной) Вы не читатель? Так я повторю: Цитата(Укушенный воблой @ Jan 19 2017, 04:18)  Причем используют неявно. Без использования операции "взятие адреса" (амперсанд)
|
|
|
|
|
Jan 19 2017, 19:42
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-07-16
Пользователь №: 92 484

|
Цитата(XVR @ Jan 19 2017, 20:21)  Так не бывает  Зачем? С Вами и так все ясно.
|
|
|
|
|
Jan 20 2017, 04:17
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(Alt.F4 @ Jan 19 2017, 09:32)  Я не против volatile, просто хочется понять, когда без него ну совсем не обойтись и код точно не будет работать. Ожидание готовности периферии - sp, twi, usart и прочее. Регистры, содержащие флаги объявлены как volatile: в противном случае неизбежно зацикливание. Но у volatile есть и обратная сторона: использовать их нужно через не-volatile копии. Совершенно искуственный, но показательный пример: Код volatile unsigned char tick = 1; volatile unsigned char res_yes_volatile=0; unsigned char res_no_volatile=0; unsigned char flag=0;
__irq void IRQ_Handler(void) { tick++; flag = tick; }
int main() { while(flag < 6) { res_yes_volatile = tick*tick; // 2*2 = 6 res_no_volatile = tick*tick; } return 0; } Имеем в итоге: 1. вечный цикл из-за того, что flag не объявлена как volatile. 2. второй оператор умножения в цикле компилятор просто выбросил за ненадобностью. 3. команды для первого оператора цикла компилятор добросовестно сгенерировал, но так как volatile-ный tick использован напрямую, без посредников, возможны коллизии типа 2*2 = 6, 5*5 = 30. Код // 16 int main() // 17 // 18 { main: LDR.N R0,??DataTable1 // 19 // 20 // 21 while(flag < 6) LDRB R1,[R0, #+3] CMP R1,#+6 BGE.N ??main_0 // 22 { // 23 res_yes_volatile = tick*tick; ??main_1: LDRB R1,[R0, #+0] LDRB R2,[R0, #+0] MULS R1,R2,R1 STRB R1,[R0, #+1] // 24 res_no_volatile = tick*tick; LDRB R1,[R0, #+0] LDRB R1,[R0, #+0] B.N ??main_1 // 25 } // 26 // 27 return 0; ??main_0: MOVS R0,#+0 BX LR ;; return
Сообщение отредактировал aiwa - Jan 20 2017, 04:18
|
|
|
|
|
Jan 20 2017, 09:40
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(Укушенный воблой @ Jan 19 2017, 23:30)  Вы ошибаетесь. Просто я не хочу обсуждать вкус устриц с человеком, который устрицу не отличает от камбалы. Вы своим ответом показали, что вы все же хам, причем необразованный. Ляпнули какую то глупость, на просьбу привести пример - не привели, в оскорбили собеседника. Цитата Поэтому чего я у вас буду чего-то спрашивать? Это я у вас спрашивал. Не в состоянии ответить - не отвечайте, оскорблять зачем? Цитата Просто я не хочу обсуждать вкус устриц с человеком, который устрицу не отличает от камбалы. Это уже прямое оскорбление. Откуда вам знать мой уровень? Вы форум смортели, прежде чем на человека наезжать? Требую публичного извенения.
|
|
|
|
|
Jan 20 2017, 10:28
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(jcxz @ Jan 20 2017, 12:03)  Здесь компилятор вроде как должен выдать варнинг. Значит так лучше не писать. Да, забыл добавить. Компилятор выдает варнинг: "undefined behavior: the order of volatile accesses is undefined in this statement"
|
|
|
|
|
Jan 20 2017, 11:19
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(XVR @ Jan 20 2017, 14:40)  Требую публичного извенения. Ну вы нашли, с кем схлестнуться. Это же, судя по всему, Доктор ТуамОсес, он же Дон Амброзио, он же avr123, он же [ещё куча ников]. Это известный тролль, не тратьте на него своё время и нервы.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jan 20 2017, 20:21
|

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

|
Цитата(AHTOXA @ Jan 20 2017, 13:19)  Ну вы нашли, с кем схлестнуться. Это же, судя по всему, Доктор ТуамОсес, он же Дон Амброзио, он же avr123, он же [ещё куча ников]. Это известный тролль, не тратьте на него своё время и нервы. Да, это именно этот персонаж.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 21 2017, 09:09
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-07-16
Пользователь №: 92 484

|
Цитата(aiwa @ Jan 20 2017, 05:17)  2. второй оператор умножения в цикле компилятор просто выбросил за ненадобностью. Выключите оптимизацию в опциях компилятора
|
|
|
|
|
Jan 25 2017, 15:51
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-07-16
Пользователь №: 92 484

|
Нет. Если с умом подходить к разработке программы то всякие "танцы с бубном" вроде включения "оптимизации лэвел 4" не нужны. Ибо когда разработчик начинает "танцы с бубном" - это означает, что он фактически расписался в своей некомпетентности и что ситуация вышла из под контроля.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|