реклама на сайте
подробности

 
 
> __LDREX __STREX в STM32F407
_lexa_
сообщение Jun 4 2017, 13:18
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 23
Регистрация: 23-03-15
Пользователь №: 85 852



Всем доброе время суток!

IDE - IAR+плагин IAR для eclipse+eclipse.
Решил проверить, как работает синхронизация с использованием __LDREX/__STREX. Пишем следующий код
CODE
typedef struct
{
...
volatile unsigned long sync; //переменная для синхонизации доступа к данному элементу
} burst_measur;

burst_measur cur_mes;

void mpu_cfg_test()
{
//настраиваем область внутренней RAM, как разделяемую
__DMB();
SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
MPU->CTRL = 0U;

MPU->RNR = 0UL;
MPU->RBAR = 0x20000000UL;
MPU->RASR = (0x10UL << MPU_RASR_SIZE_Pos) | MPU_RASR_C_Msk | MPU_RASR_S_Msk | (0x3 << MPU_RASR_AP_Pos) | MPU_RASR_ENABLE_Msk;

MPU->CTRL = MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk;
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
__DSB();
__ISB();

//выполняем запрос эксклюзивного доступа к переменной cur_mes.sync
DWORD sync=0;
sync = __LDREX(&cur_mes.sync);
__DMB();
soft_int_ini(); //настойка программного прерывания
soft_int_on(); //вызов программного прерывания
__WFI();
}


в обработчике программного прерывания:
CODE
DWORD sync;
do
{
sync = __LDREX(&cur_mes.sync);
sync++;
sync = __STREX(sync, &cur_mes.sync);
}
while (sync);


Т.е. сначала выполняется __LDREX(&cur_mes.sync), потом происходит прерывание и выполняется __LDREX(&cur_mes.sync) + __STREX(sync, &cur_mes.sync).
По всем документациям, как я их понял, __STREX(sync, &cur_mes.sync) должна возвратить "не ноль", однако возвращает "ноль".

Помогите, пожалуйста, разобраться, что я делаю не правильно?

Go to the top of the page
 
+Quote Post
10 страниц V  « < 6 7 8 9 10 >  
Start new topic
Ответов (105 - 119)
AVI-crak
сообщение Jun 10 2017, 05:04
Сообщение #106


Частый гость
**

Группа: Участник
Сообщений: 182
Регистрация: 16-10-15
Пользователь №: 88 894



Цитата(jcxz @ Jun 10 2017, 04:28) *
Может быть Вы укажете, где именно проблема в приведённом мной примере реализации кольцевого буфера?
А то Forger видимо знает, но держит эту тайну при себе biggrin.gif

Он считает что на буфер указывает всего один указатель, и при этом не совсем верно понимает слово "кольцевой".
Go to the top of the page
 
+Quote Post
Forger
сообщение Jun 10 2017, 06:27
Сообщение #107


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz @ Jun 10 2017, 00:35) *
Произойдёт прерывание, которое внутри вызовет write()? И что? Это никак не нарушит кольцо. Подумайте. rolleyes.gif

Мля, да при чем тут некий write?
Еще раз - проблема в тех кусках кода, где производится обращение к обоим указателям, а не только к одному,
Обращение к обоим указателям должно быть атомарным. К одному - нет.
Если в функциях обращения к одному указателю производится обращение и ко второму (сравниваем оба указателя для контроля переполнение/исчерпание), то этот участок тоже должен быть атомарен.
Разумеется, в данном случае речь идет про атомарность в функциях, который вызываются внутри задач, а не прерываний.
Если же делать общее кольцо про все случаи жизни, то защищать приходится все места, где производится сравнение и тем более модификация сразу обоих указателей.

Например, ваш flush. Прямо посреди операции чтения-модификации-запись произойдет прерывание, которое в данный момент работает с этой же очередью.
Поэтому эта flush должна быть атомарна, т.к. работает с двумя указателями сразу.
Атомарна в данном случае означает, что тот, кто может прервать ее не должен обращаться к этим указателям, пока не отработает flush.
Вот тут про это есть.
см. USART_FlushTxBuf
Ну и в комментариях некто указал про то, что у тут пытаюсь донести до остальных.
Ищите по поиску на той же странице "Код содержит не меньше одной ошибки. Нет гарантии атомарности изменения переменых."

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


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

Цитата(jcxz)
А то Forger видимо знает, но держит эту тайну при себе

Поясняющие картинки рисовать уже не буду. И так уже по третьему кругу объясняю.


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 10 2017, 08:07
Сообщение #108


Гуру
******

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



Цитата(Forger @ Jun 10 2017, 08:27) *
Мля, да при чем тут некий write?

А что при чём?
Я Вас уже 100500-й раз тут прошу:
Приведите пример (кодом) ситуации, при которой по Вашему будет косяк.
Уже даже и не знаю как сказать... wacko.gif
Вот так вот прямо - возьмите мой код, скопипастите сюда, и вставьте в него код (с комментом) в какой-то точке, который вызовет сбой.
Вы вообще к программированию отношение имеете? Если имеете - должны уметь читать и понимать код. Как и все тут вроде-бы. Не надо этих пространных полотенец рассуждений - по ним совершенно не понятно, что именно Вас смущает. Я специально написал конкретный простейший код кольцевого буфера, чтобы, как мне казалось, любой человек знающий си и умеющий читать, мог понять его работу.
Проблему в кольцевых буферах здесь видите один только Вы.
Ещё раз подчеркну: Корректно реализованный кольцевой буфер, отвечающим условиям изложенным мной в том сообщении, где я привёл его код, не требует дополнительных блокировок, запретов прерываний и т.п. НИГДЕ!!!
Даже в том случае (а это самый сложный случай) если пишущий процесс выполняется на одном процессоре, а читающий - на другом. Единственное дополнительное условие при этом - объект Ring должен находиться в некешеруемом регионе ОЗУ (некешеруемом в обоих процессорах). В этом случае никакие блокировки одного процессора другим как правило и невозможны.

Цитата(Forger @ Jun 10 2017, 08:27) *
Еще раз - проблема в тех кусках кода, где производится обращение к обоим указателям, а не только к одному,

Ещё раз - какие "те"?
Я в классе Ring реализовал все методы необходимые для работы с кольцевым буфером - про какой из них Вы говорите??? Мы здесь не телепаты, не можем понять что такое "те" куски кода, а что такое "не те".

Цитата(Forger @ Jun 10 2017, 08:27) *
Обращение к обоим указателям должно быть атомарным. К одному - нет.

Я подозреваю Вы не понимаете понятие "атомарность".... laughing.gif

Цитата(Forger @ Jun 10 2017, 08:27) *
Если же делать общее кольцо про все случаи жизни, то защищать приходится все места, где производится сравнение и тем более модификация сразу обоих указателей.

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

Цитата(Forger @ Jun 10 2017, 08:27) *
Например, ваш flush. Прямо посреди операции чтения-модификации-запись произойдет прерывание, которое в данный момент работает с этой же очередью.

Ещё 100500-й раз: Какой операции чтения-модификации-запись?
Вы понимаете смысл ключевого слова "class" языка си? А понятие прав "public", "private" класса Вам знакомо?
Ещё и ещё раз прочитайте мой код: я не зря их там поставил. Т.е. - для работы с объектом класса можно пользоваться только его методами. Среди методов есть модифицирующие, а есть readonly-методы.
Какой именно из них Вас не устраивает?
flush() туда тоже можно добавить и к нему будут относиться все ограничения указанные для других:
из пишущего процесса можно вызывать только функции, меняющие указатель wpos;
из читающего процесса можно вызывать только функции, меняющие указатель rpos.
flush() какой указатель меняет?
readonly-методы можно вызывать из любого процесса, читающего или пишущего, без ограничений.

Цитата(Forger @ Jun 10 2017, 08:27) *
см. USART_FlushTxBuf
Ну и в комментариях некто указал про то, что у тут пытаюсь донести до остальных.
Ищите по поиску на той же странице "Код содержит не меньше одной ошибки. Нет гарантии атомарности изменения переменых."

И при чём тут чей-то говнокод? Очевидно, что человек написавший его, совершенно не понимает принципов многозадачной работы. Это видно уже сразу в самом начале.
Я же привёл Вам свой пример кода, абсолютно корректный и не требующий блокировок.
Моя реализация не имеет с тем говнокодом абсолютно ничего общего. Если Вы конечно умеете читать и понимать си-код... laughing.gif
Если видите в моём коде проблему - укажите где? Кодом пожалуйста.
Go to the top of the page
 
+Quote Post
Forger
сообщение Jun 10 2017, 08:09
Сообщение #109


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz)
...

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


зы. Ring::flush() { rpos = wpos; } неатомарна, после чтения wpos, может произойти прерывание, где может измениться wpos или rpos и вернуться назад.
По атомарностью в данном контексте я имею ввиду, непрерывность выполняемого кода, т.е. код, который не может прервать другой код, обращающийся к этим же данным (запись или чтение).

Цитата(jcxz)
Вы понимаете смысл ключевого слова "class" языка си? А понятие прав "public", "private" класса Вам знакомо?

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


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 10 2017, 08:24
Сообщение #110


Гуру
******

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



Цитата(Forger @ Jun 10 2017, 10:09) *
зы. Ring::flush() { rpos = wpos; } неатомарна, после чтения wpos, может произойти прерывание, где может измениться wpos или rpos и вернуться назад.

А Вы извините читать вообще умеете? Или "чукча не читатель"? biggrin.gif
Прочитайте это ещё раз и ещё и ещё много раз до полного просветления. Я это уже многократно повторил.
Цитата(jcxz @ Jun 10 2017, 10:07) *
из пишущего процесса можно вызывать только функции, меняющие указатель wpos;
из читающего процесса можно вызывать только функции, меняющие указатель rpos.

Ваша ситуация - невозможна.
Ибо ТОЛЬКО ОДИН процесс должен менять rpos. А Вы пишете про его модификацию В ДВУХ процессах.

Цитата(Forger @ Jun 10 2017, 10:09) *
Предлагаю избегать оскорблений подобного рода. Мы тут не впервые сремся общаемся.

Это не оскорбление, а констатация факта. Так как я явно в реализации класса указал, что с ним могут работать только его методы. А вы пишете про какие-то другие модификации-записи.
К его методам можно добавить и flush().
Go to the top of the page
 
+Quote Post
Forger
сообщение Jun 10 2017, 08:29
Сообщение #111


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz @ Jun 10 2017, 11:15) *
Прочитайте это ещё раз и ещё и ещё много раз до полного просветления. Я это уже многократно повторил.

Аналогично адресую это Вам.
У меня класс Queue сделан совершенно аналогично вашему (у меня он вообще шаблон).
Раньше там не было ни каких критических секций.
Я напоролся на это в одном из проектов. Очень долго искал косяк. Проклял все на свете.
Но к счастью нашел. Исправил - прерывания запрещаются на время анализа исчерпания/переполнения и в этой самой flush.
Прерывания запрещаются лишь одно - то, которое обращается в этой очереди.
Проблема сразу ушла.

Вот под руками есть старая реализация этой Queue из одного из проектов, тут критические секции добавлены в лоб:
CODE

template <class Item, unsigned int SIZE>
class Queue
{
public:
Queue(void) : head(0), tail(0), freeSize(SIZE) { }

class CriticalSection // No nested, but very fast - 3 CPU cycles !!!
{
public:
inline CriticalSection() __attribute__((always_inline)) { __disable_irq(); }
inline ~CriticalSection() __attribute__((always_inline)) { __enable_irq(); }
};

void clear(void)
{
CriticalSection criticalSection;
head = 0;
tail = 0;
freeSize = SIZE;
}

bool isPutDone(const Item & item)
{
CriticalSection criticalSection;
if (freeSize == 0) return (false);
queue[head] = item;
head = head + 1;
if (head >= SIZE) head = 0;
freeSize = freeSize - 1;
return (true);
}

bool isGetDone(Item & item)
{
CriticalSection criticalSection;
if (freeSize >= SIZE) return (false);
item = queue[tail];
tail = tail + 1;
if (tail >= SIZE) tail = 0;
freeSize = freeSize + 1;
return (true);
}

void tryToGet(Item & item) { isGetDone(item); }

private:
Item queue[SIZE];
volatile unsigned int head;
volatile unsigned int tail;
volatile unsigned int freeSize;
};


Цитата(jcxz @ Jun 10 2017, 11:24) *
Ибо ТОЛЬКО ОДИН процесс должен менять rpos. А Вы пишете про его модификацию В ДВУХ процессах.

Тут не все так просто. Речь не просто про изменение (запись), а про обращение (чтение). Это очень важно!
Если бы было просто rpos = некая константа, то проблем нет, но у вас rpos=wpos, т.е. обращение происоходит к ОБОИМ индексам/указателям.
Этот процесс копирования нельзя прерывавать тем, кто может обращаться к этим же данным.
Это букварные истины ))


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 10 2017, 08:36
Сообщение #112


Гуру
******

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



Цитата(Forger @ Jun 10 2017, 10:29) *
Вот под руками есть старая реализация этой Queue из одного из проектов, тут критические секции добавлены в

Да, Ваша реализация требует критической секции. Потому что у Вас в методах изменяются сразу два члена Queue.
Моя реализация - не требует, так как в каждом методе изменяется только один член.
Странно что Вы не видите отличий в Вашей и моей реализации..... laughing.gif
Это ключевое различие. Если Вы его наконец-то поймёте, то сможете писать корректное многозадачное ПО.

И Вы похоже не поняли почему Ваша реализация требует критической секции. Это не потому что там чтение и запись указателей. А потому что Вы в каждом методе изменяете два члена-указателя, а значит эти модификации неатомарны.
В моих методах в каждом изменяется не более одного члена-указателя, а значит их модификации атомарны.

PS: Признаю что погорячился насчёт вызова readonly-методов из третьего процесса - это в моей реализации недопустимо. Допустимы вызовы только из двух процессов: пишущего в Ring и читающего в Ring.
Go to the top of the page
 
+Quote Post
AVI-crak
сообщение Jun 10 2017, 10:36
Сообщение #113


Частый гость
**

Группа: Участник
Сообщений: 182
Регистрация: 16-10-15
Пользователь №: 88 894



Разве ерорная ситуация не должна обрабатываться системными вызовами? С закрытием потока и всех его хвостов... И кстати, получить сбой на кольцевом буфере - это очень сильно постараться нужно, прямо таки саботаж устроить.
Go to the top of the page
 
+Quote Post
Forger
сообщение Jun 10 2017, 11:18
Сообщение #114


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz @ Jun 10 2017, 11:36) *
Признаю что погорячился насчёт вызова readonly-методов из третьего процесса - это в моей реализации недопустимо. Допустимы вызовы только из двух процессов: пишущего в Ring и читающего в Ring.

АЛЛИЛУЙЯ!!! Наконец-то мои слова начинают доходить!

Цитата
Да, Ваша реализация требует критической секции. Потому что у Вас в методах изменяются сразу два члена Queue.

Любая реализация требует подобных действий, если она должна контролировать исчерпание/переполнение буфера.
У вас free и cnt - отдельные функции, у меня - входят в состав соотв. методов записи/чтения очереди.
Т. к. контроль переполнения нужно делать перед тем, как что класть в очередь и соотв. контроль исчерпания перед тем, к брать данные из очереди.
То эти функции всегда нужно вызывать перед тем, как обращаться к очереди.
А функции должны быть атомарными, т. к. обращаются к обоим указателям.
Я привел старую реализацию, а в нынешней у меня защищены только части, где производится обращение (неважно что - запись или чтение) в ОБОИМ указателям.
Причем, запрещается прерывание от владельца этой очереди только там, где оно вызывается в фоне.

Цитата
Моя реализация - не требует, так как в каждом методе изменяется только один член.

Если никогда не вызывать cnt и free в коде, то это действительно так. С этим я и не спорил.

Цитата
Странно что Вы не видите отличий в Вашей и моей реализации..... laughing.gif Это ключевое различие.

Странно другое - вы упорно не слышите меня, но судя по всему, лед тронулся sm.gif


Цитата
Если Вы его наконец-то поймёте, то сможете писать корректное многозадачное ПО.

Мне не нравится ваша манера вести диалог подобным образом, поэтому настоятельно прошу избегать при общении со мной подобных выражений!

Цитата
И Вы похоже не поняли почему Ваша реализация требует критической секции.

Вот я как раз понимаю прекрасно.
В этой старой реализации это было сделано в лоб. Задача была решена.
В нынешней реализации это сделано менее "топорно".

Цитата
В моих методах в каждом изменяется не более одного члена-указателя, а значит их модификации атомарны.

Это неправда - в методах free и cnt обращение производится к обоим указателям. По сути оба этих метода должны целиком находится в критических секциях.
И я уже повторял, что не имеет значения - модификация (запись) или обращение (чтение).

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


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 10 2017, 15:42
Сообщение #115


Гуру
******

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



Цитата(Forger @ Jun 10 2017, 13:18) *
Если никогда не вызывать cnt и free в коде, то это действительно так. С этим я и не спорил.
...
Важно, чтобы этот процесс обращения (чтение или запись) к ОБОИМ указателям был неразрывен для прерывания, которое может обращаться к любому из этих указателей!

Нет и нет. Вы так ничего и не поняли. Живёте на какой-то своей волне. Все мои просьбы указать конкретное место где в моих функциях будет сбой и при каком конкретно событии - Вы проигнорировали. Другие аргументы - тоже игнорируете. И продолжаете нести бред...
Из чего я делаю вывод: Вы не способны понять работу кольцевого буфера. sad.gif
Любой, давший себе хоть немного труда подумать, уже всё понял. Но вы даже не пытаетесь понять, вот в чём беда...
Что-ж - может когда-нить до Вас дойдёт.
В данное время считаю дальнейшие попытки что-то Вам объяснить бесполезными - лепите свои критические секции хоть в каждой строке - Вы не способны обучаться... laughing.gif
Go to the top of the page
 
+Quote Post
Forger
сообщение Jun 10 2017, 15:48
Сообщение #116


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz @ Jun 10 2017, 18:42) *
...продолжаете нести бред... Вы не способны понять ... может когда-нить до Вас дойдёт... Вы не способны обучаться...

Мир не меняется - когда проигрыш в споре становится очевидным, в ход идут оскорбления и унижения.


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

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





--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jun 10 2017, 18:39
Сообщение #117


фанат дивана
******

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



Цитата(Forger @ Jun 10 2017, 20:48) *
участок кода, где производится чтение ОБОИХ указателей должен быть атомарным для того, кто может обращаться к одному из этих указателей и тем более его изменить.


Попробую и я объяснить вамsm.gif

1. Есть два указателя - put и get. При их равенстве - буфер пуст. При put+1 == get - буфер полон.
2. Запись производится только в одном потоке, чтение - только в другом.
3. Записывающий поток перед записью проверяет наличие свободного места. Для этого он считывает в промежуточные переменные оба указателя, затем производит сравнение. Если свободное место есть, то всё в порядке - поток производит запись, увеличивает промежуточную переменную, содержащую указатель на позицию записи, и записывает её в put. Если записывающий поток в любом месте этой процедуры будет прерван читающим потоком, то это не собьёт алгоритм. Если он будет прерван до считывания значения get в промежуточную переменную, то поток будет иметь дело с уже обновлённой переменной, если нет - то с предыдущим её значением. В любом случае запись отработает корректно.
4. Читающий поток поступает так же - перед считыванием копирует put и get во временные переменные, производит проверки, если буфер не пуст - считывает элемент, модифицирует временную переменную, содержащую указатель на чтение и записывает её в get. Если в любом месте этой процедуры читающий поток будет прерван записывающим потоком, то это не нарушит логики операции. Возможны два варианта - во временную переменную, содержащую позицию записи, будет считано либо значение put, либо put+1. В обоих случаях чтение пройдёт корректно, будет считан нужный элемент (или не считан, если буфер пуст).

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

Надеюсь, я смог объяснить.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
Forger
сообщение Jun 10 2017, 19:19
Сообщение #118


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(AHTOXA @ Jun 10 2017, 21:39) *
Для этого он считывает в промежуточные переменные оба указателя,...
Если он будет прерван до считывания значения get в промежуточную переменную, то поток будет иметь дело с уже обновлённой переменной, если нет - то с предыдущим её значением.

Есть еще третий вариант - прерывание возникнет между чтениями этих двух указателей.
Ведь читаются они не одновременно, а поочередно.

Для примера, имеем 16-битный указатель на 8-битном проце (некий МК типа PIC или STM8).
Если прерывание, где используется и тем более изменяется значение этого указателя, возникнет между записями старшего и младшего байта этого 16-битного указателя в основном кода, то будет беда.
Это - очень простой пример, но он наглядно демонстрирует что я имею ввиду.

Цитата
Надеюсь, я смог объяснить.

Это все понятно, и спору нет. Об этом выше и сказал.
Я же говорю о ситуациях, когда прерывание возникает при чтении ОБОИХ указателей МЕЖДУ этими обращениями. Ведь это происходит не атомарно за один такт, а поочередно.
Однажды, я наступил на эти грабли и решил задачу в лоб - критическими секциями. Это, конечно, помогло, но потом я исправил и сделал это более аккуратно и деликатно.

В данном случае с кольцевым буфером (речь про 32-битные ARMы), можно избежать критических секций вообще, если оба указателя являются 16-битными индексами массива кольцевого буфера, но находятся в памяти рядом.
Т.е. можно их вычитать или даже записать одновременно, т. е. атомарно как целое 32-битное слово.
Фактически, атомарность доступа реализуется средствами самого ядра.
Вряд ли где-то на обычном МК понадобится буфер более чем на 65536 элементов, поэтому будет вполне достаточно 16-битных индексов вместо 32-битных указателей.

Простая задачка: на 8-битном МК имеем кольцевой буфер с 16-битными указателями (или индексами).
1) прямо внутри flush (пока изменяются указатели) возникнет прерывание и дописывает в буфер новые данные
2) процесс анализа оставшего места в буфере в основном коде прерван соотв. прерыванием, где в буфер докидываются новые данные.
В каких случаях следует беспокоится об атомарности доступа к этим указателям? И следует ли это делать вообще?


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
AVI-crak
сообщение Jun 10 2017, 19:40
Сообщение #119


Частый гость
**

Группа: Участник
Сообщений: 182
Регистрация: 16-10-15
Пользователь №: 88 894



Может на Си показать кусок кода?


CODE
#define buf_zize (127)
uint8_t _std_out_buffer[buf_zize];
uint8_t _std_in_buffer[buf_zize];
uint8_t m_mk_buf[buf_zize + 1];
#pragma pack(push, 4)
struct _stdout
{
const uint16_t length;
const volatile uint16_t tail;
volatile uint16_t head;
uint16_t mode;
volatile uint8_t *ptr;
}_eb_monitor_stdout ={buf_zize,0,0,1,&_std_out_buffer[0]};

struct _stdin
{
const uint16_t length;
volatile uint16_t tail;
const volatile uint16_t head;
uint16_t mode;
const volatile uint8_t *ptr;
}_eb_monitor_stdin ={buf_zize,0,0,1,&_std_in_buffer[0]};
#pragma pack(pop)

void monitor_fining (void)
{
uint16_t tmp = 0;
do
{
m_mk_buf[tmp++] = 0x20;
}while (tmp != buf_zize);
};


/// печать строки
void monitor_print (uint8_t* text)
{
uint16_t temp_t = 0;
if (text[temp_t] == '\0') return;
uint16_t temp_h = _eb_monitor_stdout.head;
uint16_t temp_l;
do
{
temp_l = temp_h; temp_h++;
if (temp_h == buf_zize) temp_h = 0;
if (_eb_monitor_stdout.tail != temp_h ) _eb_monitor_stdout.ptr[temp_h] = text[temp_t++];
else
{
_eb_monitor_stdout.head = temp_l;
while (_eb_monitor_stdout.tail != temp_l ) sTask_skip();// Delay(1000);// там занято
_eb_monitor_stdout.ptr[temp_h] = text[temp_t++];
_eb_monitor_stdout.head = temp_h;
}
}while (text[temp_t] != '\0');
_eb_monitor_stdout.head = temp_h; __DSB();
};

uint8_t *monitor_scan (uint8_t *text)
{
uint16_t temp_h = _eb_monitor_stdin.head;
uint16_t temp_l = _eb_monitor_stdin.tail;
uint16_t temp_x = 0;
if(temp_l == temp_h) { text[0] = 0; return text;};
while (temp_l != temp_h)
{
temp_l++; if (temp_l == buf_zize) temp_l = 0;
text[temp_x++] = _eb_monitor_stdin.ptr[temp_l];
}
_eb_monitor_stdin.tail = temp_l;
text[temp_x] = 0;
return text;
}


Это функции для печати в терминал через программатор. Глюков и зависаний не обнаружено, печатает отдельный процесс, в случае переполнения - остаётся в вечном ожидании, с последующим вытеснением. То-есть программа на мк не падает в случае автономного исполнения.
Оба указателя читаются, и участвуют в управлении - но писать можно только один. Второй указатель переписывает отладчик. То самое в структуре под "const volatile" - меняется сторонним процессом.
Инициализация один раз, при старте.
Есть варианты с динамическим запуском в произвольном месте, но для терминала через программатор - получается слишком сложно для примера.

Сообщение отредактировал AVI-crak - Jun 10 2017, 19:44
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jun 10 2017, 19:54
Сообщение #120


фанат дивана
******

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



Цитата(Forger @ Jun 11 2017, 00:19) *
Есть еще третий вариант - прерывание возникнет между чтениями этих двух указателей.
Ведь читаются они не одновременно, а поочередно.

Это без разницы. Потому что в этом прерывании возможны только транзакции пусто-не пусто для записывающего потока и не пусто-пусто - для читающего. А другой поток делает лишь проверку на пусто/не пусто (которая выливается в проверку на равенство).

Цитата(Forger @ Jun 11 2017, 00:19) *
Для примера, имеем 16-битный указатель на 8-битном проце (некий МК типа PIC или STM8).
Если прерывание, где используется и тем более изменяется значение этого указателя, возникнет между записями старшего и младшего байта этого 16-битного указателя в основном кода, то будет беда.

Да нет же, всё будет нормально.

Для записывающего потока: нет равенства [put+1 == get] - пишем. (Тут нас может прервать читающий поток и он может изменить ситуацию только в одном направлении - буфер полон -> буфер не полон. То есть [put+1 == get] => [put+1 != get]). Если буфер был не полон - то он и останется не полон, независимо от того, прервали нас или нет. Если буфер был полон, то тут зависит от того, успели мы считать указатель get целиком или нет. Если успели - мы посчитаем, что буфер полон, и записи не будет. Это корректное поведение. Если читающий поток успел модифицировать хотя бы один байт из get, то буфер перестанет быть полон, и мы с полным правом запишем туда байт. И это тоже корректно.

Для читающего потока: нет равенства put == get - читаем. Соответственно, тут наоборот, прервавший нас записывающий поток может изменить ситуацию только в направлении буфер пуст => буфер не пуст. То есть, [put == get] => [put != get]. Все рассуждения полностью аналогичны предыдущему случаю.

И совершенно неважно, какими порциями записываются эти put и get.

Вообще я удивлён. Вы вроде такой опытный по вашим словам разработчик, и не знаете этот широко известный приём. Он применялся ещё на MCS51, причём очень широко.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post

10 страниц V  « < 6 7 8 9 10 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 22:14
Рейтинг@Mail.ru


Страница сгенерированна за 0.01568 секунд с 7
ELECTRONIX ©2004-2016