Подтвердить что ты не робот

Является ли С++ 11 атомным <T> полезным с mmap?

Я хочу добавить сетевой контроль нескольких параметров, используемых службой (daemon), запущенной в встроенной системе Linux. Там нет необходимости в процедурных вызовах, каждый параметр может быть опробован очень естественным образом. Общая память кажется хорошим способом сохранить сетевой код вне демона и ограничить общий доступ к тщательно контролируемому набору переменных.

Так как я не хочу, чтобы частичные записи вызывали видимость значений, которые никогда не были написаны, я думал об использовании std::atomic<bool> и std::atomic<int>. Однако я обеспокоен тем, что std::atomic<T> может быть реализован таким образом, который работает только с потоками С++ 11, а не с несколькими процессами (возможно, даже с потоками ОС). В частности, если реализация использует любые структуры данных, хранящиеся вне блока разделяемой памяти, в многопроцессорном сценарии это не сработает.

Я вижу некоторые требования, которые предполагают, что std::atomic не будет содержать встроенный объект блокировки или указатель на дополнительные данные:

Атомные интегральные специализации и специализация atomic<bool> должны иметь стандартную компоновку. Каждый из них имеет тривиальный конструктор по умолчанию и тривиальный деструктор. Каждый из них должен поддерживать синтаксис синтаксической инициализации.

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

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

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

Является ли реализация atomic<T> разрешена делать вещи, которые несовместимы с общей памятью между процессами, или есть другие правила, которые делают ее безопасной?


Я только заметил, что тривиальная конструкция по умолчанию оставляет объект в состоянии неготовности, и требуется вызов atomic_init. И в стандарте упоминается инициализация замков. Если они хранятся внутри объекта (и динамическое распределение памяти кажется невозможным, поскольку деструктор остается тривиальным), то они будут разделяться между процессами. Но меня все еще беспокоит возможность глобального мьютекса.

В любом случае гарантирование одного вызова atomic_init для каждой переменной в общей области кажется трудным... поэтому я полагаю, что мне нужно будет уклониться от атомных типов С++ 11.

4b9b3361

Ответ 1

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

Вот что я нашел:

  • Стандарт С++ 11 определяет новую модель памяти, но в ней нет понятия "процесс" на уровне ОС, поэтому все связанные с многопроцессорностью нестандартные.

  • Однако в разделе 29.4 "Свойство без блокировки" стандарта (или, по крайней мере, проект I, N3337) заканчивается этой запиской:

    [Примечание. Операции, которые блокируются, также должны быть без адресов. То есть, атомарные операции на одном и том же расположение памяти через два разных адреса будет связываться атомарно. Реализация не должна зависят от любого состояния процесса. Это ограничение позволяет осуществлять обмен данными по памяти, которая отображается в процесс более одного раза и по памяти, которая разделяется между двумя процессами. - конечная нота]

    Это звучит очень многообещающе.:)

  • Эта заметка, по-видимому, получена из N2427, которая еще более ясна:

    Чтобы облегчить взаимодействие между процессами через разделяемую память, мы намерены, чтобы операции блокировки также были без адресов. То есть, атомарные операции в одном и том же месте памяти через два разных адреса будут сообщаться атомарно. Реализация не должна зависеть от какого-либо состояния процесса. Хотя такое определение выходит за рамки стандарта, четкое изложение нашего намерения позволит переносимое выражение класса уже существующих программ.

    Итак, кажется, что да, все блокированные операции должны работать в этом точном сценарии.

  • Теперь операции с std::atomic<type> являются атомарными, но они могут или не могут быть заблокированы для конкретного type, в зависимости от возможностей платформы. И мы можем проверить любую переменную x, вызвав x.is_lock_free().

  • Так почему я написал, что я не буду зависеть от этого? Я не могу найти какую-либо документацию для gcc, llvm или кого-либо еще, что явное об этом.

Ответ 2

До С++ 11 в стандарте не указывалось, как несколько потоков совместно используют память, поэтому мы написали программы с несколькими потоками, которые опирались на поведение, специфичное для реализации. В стандарте по-прежнему не указано, как взаимодействуют процессы с разделяемой памятью или, если хотите, потоки, которые только частично используют память. Независимо от того, что вы в конечном итоге делаете, вы будете полагаться на конкретные гарантии.

Тем не менее, я думаю, что реализация, которая поддерживает разделяемую процессами память, попытается создать механизмы синхронизации потоков, такие как атомы, которые можно использовать в памяти процессов для синхронизации процессов. По крайней мере, я думаю, что было бы сложно создать блокирующую реализацию std:: атомной специализации, которая не работает правильно кросс-процесс.