Атомно перезаписать указатель - PullRequest
0 голосов
/ 01 июня 2018

Я нашел вопросы, похожие на тот, который у меня есть (т.е. Считается ли изменение указателя атомарным действием в C? ), но я не смог найти ничего, что давало бы мне определенное да / нетответ, когда дело доходит до написания указателя.

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

Например,

struct my_struct {
    void *ptr;
};
struct my_struct *my_struct[10];

Поля указателя каждой структуры считываются и записываются потоком.

Например,

uint8_t index;
for(index = 0; index < 10; index++)
{
    if(my_struct[index]->ptr != NULL)
    {
        my_struct[index]->ptr = NULL;
    }
}

Периодически происходит прерывание, которое считывает (не записывает, и я не могу использовать блокировку, потому что обработчик критичен по времени) к одному или нескольким указателям, хранящимся в моем массиве структур.

Например,

void *ptr = my_struct[2]->ptr;

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

my_struct[index]->ptr = NULL;

Но могу ли я быть уверен, что если произойдет прерывание с сообщением «my_struct [index] -> ptr», оно не будет повреждено?Я подозреваю, что это должно быть правдой, поскольку (1) указатель должен полностью вписываться в регистр (по крайней мере, для моей цели, MSP430) и (2) запись одного регистра, скорее всего, является атомарной инструкцией.Верны ли мои рассуждения?

Ответы [ 4 ]

0 голосов
/ 01 июня 2018

Поскольку речь идет о C, большинство опубликованных ответов - бессмыслица.Неважно, насколько велик тип указателя, и что MPC430 является 16-битным MCU, как и 16-битная адресная шина.Думать, что это как-то важно для того, чтобы сделать код C атомарным, просто наивно.

Когда вы пишете C, любая переменная (или указатель) может храниться где угодно: в регистрах, в стеке или полностью оптимизироваться.Программист не имеет никакого контроля над этим, и это хорошо.

Всякий раз, когда у вас есть шанс, что переменная хранится в стеке, у вас также есть шанс для нескольких инструкций.Если задано a = b, то либо a, b, либо оба могут быть сохранены где угодно, если вообще есть.Если какой-либо из них хранится в стеке, у вас есть большой шанс, что сгенерированный машинный код приведет к чему-то вроде:

  • Инструкция 1: «сохранить данные из стека в регистре»
  • Инструкция 2: «сделать что-нибудь с регистром».

Этого сценария достаточно, чтобы нарушить атомарность - аппаратное обеспечение ЦП не имеет значения.Даже в том случае, когда ядро ​​поддерживает запись непосредственно из стека в другую память, нет гарантии, что это будет сделано в одной инструкции.Точка.

И даже если вы можете с разборкой проверить, что машинный код атомарный, это не обязательно стабильное положение дел.Внесите изменения в код, добавьте дополнительные переменные, снова свяжите и внезапно код, который был атомарным до того, как он стал атомарным, перестал быть атомарным, или наоборот. Только существующие гарантии атомарности в Cязык:

  • Ключевое слово _Atomic из C11.
  • Встроенный ассемблер, где вы пишете все вручную.
  • Использование защитных механизмов, таких как мьютекс, семафоры, прерыванияотключить и т. д.
0 голосов
/ 01 июня 2018

Оперативная память для этого чипа встроена в чип.Компилятор согласен с процессором в отношении размера указателя, я думаю, с вами все будет в порядке.
Кеша нет, так что вам даже не придется беспокоиться об ограждениях для синхронизации доступа.

РЕДАКТИРОВАТЬ: множество другихответы здесь +1 ко всем.Вопрос OP - это скорее вопрос компилятора MSP430 / gcc, чем вопрос о языке C, и, поспешно, вчера вечером перед тем, как выключить свет и лечь спать, я дал вышеуказанный ответ.У меня нет предыдущего опыта работы с MSP430, поэтому я подключился к сети и сделал несколько замечаний, попросил OP проверить пару констант в их среде и пришел к выводу, что им, вероятно, не о чем беспокоиться в заявленном сценарии.,Я привык работать со встроенными компиляторами C, которые едва соответствуют стандартам K & R, намного меньше C99 или C11, но этот опыт на самом деле предшествует C11, поэтому я не думал спросить, было ли доступно ключевое слово _Atomic (я должен был!).Итак, вот еще один вариант:

Если вы можете объявить _Atomic(void*) ptr;, сделайте это во что бы то ни стало.Это обеспечит необходимое выравнивание и сгенерирует код, который пишет и читает значение указателя атомарно.Как указывает @Lundin, это единственная надежная вещь в C, когда дело доходит до атомарности.При отсутствии _Atomic, assert(sizeof(void*) == 2) и assert(&(my_struct[index]->ptr) % 2 == 0), последний будет гарантировать, что значение указателя будет сохранено в выровненном адресном местоположении.Если / когда эти утверждения не выполняются, вы рискуете прочитать частично записанное значение указателя из-за смещения или размера указателя, превышающего размер слова процессора.Даже эти утверждения не являются гарантией, поскольку они верны только для кода, скомпилированного с определенным DEBUG.Если вы чувствуете необходимость всегда проверять эти ограничения, используйте вместо этого if(expression).

@CL. В отношении ключевого слова volatile также следует принять близко к сердцу, так как компилятор может свободно оптимизировать иПереупорядочив доступ, возможно, что подпрограмма прерывания никогда не увидит действительного значения указателя, и если эти данные не были инициализированы в NULL до какого-либо использования в вашем коде, что может стать причиной некоторых очень серьезных, трудно диагностируемых ошибок.Это маловероятный сценарий для простых микросхем без кеша и спекулятивных конвейеров выполнения, но его нельзя исключать.Поэтому используйте ключевое слово volatile.

0 голосов
/ 01 июня 2018

TelosB использует msp430 Series 1 MCU, для которого все указатели являются 16-битными переменными.(Существуют другие серии msp430, в которых это невозможно удержать.) Обычно доступ к 16-разрядным переменным в msp430 является атомарным.Однако компилятор будет разделять доступ к указателю в нескольких инструкциях на случай, если есть причина подозревать, что доступ может быть невыровненным (источник: форум TI ): в отличие от x86, msp430 может осуществлять только доступ к словамв выровненной памяти.

В приведенном выше примере компилятору нет причин подозревать это, поскольку указатель является частью структуры, объявленной без модификаторов выравнивания.Итак, история в том, что вы почти наверняка в безопасности.Тем не менее, если вы запрашиваете формальные гарантии, вероятно, их нет.Кроме того, вы должны использовать volatile по причинам, указанным в CL.

0 голосов
/ 01 июня 2018

В архитектуре MSP430 (и с любым из компиляторов C, которые вы могли бы использовать), чтение / запись указателя действительно атомарны.Поэтому для MSP430 не существует специального std::atomic (или эквивалентного) кода поддержки.

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

В вашем случае все, что вам нужно, это то, что компилятор может не знать о параллельных доступах и переупорядочивать свои собственные обращения к переменной.Другими словами, присвоение ptr может быть изменено (или, в крайних случаях, оптимизировано), если вы не используете доступ volatile.Вы можете либо сделать ptr сам volatile (void * volatile ptr;), либо добавить volatile при назначении .

...