Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.95/56: Рейтинг темы: голосов - 56, средняя оценка - 4.95
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700

Синхронизация доступа к разделяемой памяти

21.02.2017, 12:08. Показов 12094. Ответов 73
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Когда потоки являются дочерними по отношению к процессу тут все просто - объект мьютекса находится в общей памяти и используя этот объект можно делать mutex.lock() определенной секции а при завершении работы mutex.unlock();

А как синхронизировать доступ к данным shared memory между процессами?
Подозреваю что в таком случае мьютекс нужно хранить разделяемой памяти
Но как конкретно это реализуется плохо представляю
Подскажите пожалуйста кто знает
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
21.02.2017, 12:08
Ответы с готовыми решениями:

Реализация стека строк в разделяемой памяти (MPI)
Написать реализацию стека строк в разделяемой памяти. При запуске программа создает блок разделяемой памяти (если его еще нет) или...

Как сохранить данные контейнера в разделяемой памяти
Ищу быстрый способ обмена данными между процессами. Процессы - одинакового типа т.е одна и та же программа и все процессы на одной машине. ...

Есть ли оверхед от использования разделяемой памяти, в сравнении с глобальной?
Если оверхед есть, то по каким причинам? Я понимаю что зависит от реализации. Но если есть какие то факты/мнения буду рад ознакомиться...

73
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
23.02.2017, 12:15
Студворк — интернет-сервис помощи студентам
Renji, где-то писал, что какие-то из атомарных операций по стандарту обязаны быть Lock-free. Всё остальное можно проверить специальным интерфейсом, являются ли примитивы, работающие с конкретной переменной, Lock-free
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
23.02.2017, 12:18
Цитата Сообщение от sys_beginner Посмотреть сообщение
Плохо понял что вы имеете ввиду. Разве ему не достаточно иметь доступ к разделяемой памяти?
C++
1
2
3
sharedMap.lock();//захватили шаред-мемори
sharedMap[12]=34;//записали данные
exit(0);//грохнулись
Теперь как фоновый процесс откатит действия в строчке номер два, если он о них ни сном, ни духом?
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
23.02.2017, 12:25  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
Моя ситуация. Кто-то начал захватывать мьютекс (который является non-lock-free) и сдох.
Когда я писал навеяно, идеи с фоновым процессом еще не было В целом подумал о проблемах локов более углубленно после твоего поста

Добавлено через 1 минуту
Цитата Сообщение от Renji Посмотреть сообщение
Теперь как фоновый процесс откатит действия в строчке номер два, если он о них ни сном, ни духом?
Данные же в шареде
Разве этот процесс не может сместиться на то смещение в шареде где находятся метаданные и по ним определить где чего откатывать?

Добавлено через 3 минуты
Renji,
Ну или просто использовать атомарный булевный флаг как предлагал (гарантировать последовательность записи данных в память) Evg и перед записью данных делать его фолс а после тру. Если фолс так и остался фолсом - значит данные повреждены
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
23.02.2017, 12:26
Цитата Сообщение от sys_beginner Посмотреть сообщение
Данные же в шареде
Разве этот процесс не может сместиться на то смещение в шареде где находятся метаданные и по ним определить где чего откатывать?
Откат требует вернуть в sharedMemory[12] его старое значение. Это старое значение кто-то, где-то сохранил? Нет, мы можем поручить изменяющему шаред-мемори процессу еще и записывать куда-то что он там наизменял. Но процесс то по условию задачи может сбойнуть. А значит может и не сделать бекап. Или сделать, но неправильный.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
23.02.2017, 12:28
Цитата Сообщение от sys_beginner Посмотреть сообщение
Ну или просто использовать атомарный булевный флаг как предлагал Evg и перед записью данных делать его фолс а после тру
Это две совершенно перпендикулярные проблемы. Ограничивающий флаг (мьютекс) просто обязан быть Lock-free. Без этого ты нормальную систему не построишь никогда. Но это никак не отменяет возможности Dead-lock'ов, возникающих из-за того, что завис процесс, захвативший мьютекс. Так же это никак не отменяет проблемы, что после прибития зависшего процесса данные надо как-то откатить (точнее, как-то надо понять, что и как откатывать)
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
23.02.2017, 12:32  [ТС]
Evg,
Но тем не менее проблема с откатом частично же решается если мы гарантировано знаем что последовательность записи данных в памяти будет одинаковой. Если там фолс - значит такие данные читать нельзя... И вообще не факт что может понадобится откат. Ну похерились данные ну и пофиг. Такое тоже может быть. Главное знать что они повреждены.

Я не говорю что это решит проблему с мертвыми процессами которые захватили примитив но тем не менее хотя бы устранит проблему чтения кривых данных

Возвращаясь к примеру с использованием СУБД так же хочу отметить, что она тоже может "упасть" и тормознуть весь процесс...
Значит надежности все таки нет. Можно конечно создавать процессы "которые следят за процессами которые следят за следящими процессами" но то что они не рухнут все равно не гарантируется....
Наверное надо копать в сторону алгоритмов а не решений за счет вспомогательных процессов/потоков
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
23.02.2017, 12:37
Цитата Сообщение от sys_beginner Посмотреть сообщение
Возвращаясь к примеру с использованием СУБД так же хочу отметить, что она тоже может "упасть" и тормознуть весь процесс...
Может. Но СУБД одна, а процессов много. Вероятность что упадет одна СУБД, куда ниже вероятности что упадет один из сотни процессов. Плюс, СУБД скорее всего отлаживали куда дольше и тщательнее. Чай, не первый год на рынке.
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
23.02.2017, 12:39  [ТС]
Цитата Сообщение от Renji Посмотреть сообщение
Чай, не первый год на рынке.
Согласен
Но все же думаю можно решить как-то на уровне алгоритмов... пока еще не придумал т.к новичок в таких вопросах, может со временем и найду золотую середину
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
23.02.2017, 16:27
Цитата Сообщение от sys_beginner Посмотреть сообщение
Но тем не менее проблема с откатом частично же решается если мы гарантировано знаем что последовательность записи данных в памяти будет одинаковой. Если там фолс - значит такие данные читать нельзя... И вообще не факт что может понадобится откат. Ну похерились данные ну и пофиг. Такое тоже может быть. Главное знать что они повреждены.
Скажем так, что и как решается и может ли вообще решаться - знаешь только ты, как автор софта. Это же свойство конкретной задачи, а не что-то общее. Если каждый процесс захватывает семафор, переписывает миллион байтов данных (которые должны быть согласованы), то в случае зависания процесса никто уже не сможет сказать, сколько байт он успел переписать
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
25.02.2017, 13:37  [ТС]
В общем я разобрался с порядком выполнения программного кода и пожалуй поделюсь тем, что узнал.
Цитата Сообщение от Evg Посмотреть сообщение
Для этого и придумали способ, как это явно указать - добавив класс атомарных операций с семантикой, обеспечивающей требуемую упорядоченность.
На самом деле все не совсем так. Решить данную проблему можно и без таких классов, забегая вперед отмечу что для решения этой проблемы используются барьеры памяти, а все классы которые дают гарантии определенного "ордеринга" лишь реализуют эти барьеры. Применение этих барьеров сильно зависит от тех гарантий memory ordering, которые предоставляет процессор выполняющий наш программный код. Например, если нащ код исполняется в процессорах у которых строгий memory ordering, барьеры памяти использовать не нужно, потому что последовательность выполнения процессором указанных в коде операций будут в том же порядке что гарантируется самим процессором. Но жесткие гарантии как правило мешают оптимизации и соответственно такие процессоры работают медленнее чем те, у которых гарантии в отношении memory ordering слабее. Семантика relaxed о которой ты говорил как правило не обеспечивает вообще никаких гарантий последовательности доступа к данным.

Процессор и его кеши
Как известно у каждого ядра есть свой кеш, который это ядро использует для работы с данными потому что лазить в RAM на каждый чих очень дорого. Но данные с точки зрения системы одни, и эти данные могут использовать несколько потоков одновременно. Соответственно кешам ядер нужно каким то образом синхронизироваться.

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

Процесс синхронизации кешей
Когда другое ядро получает сообщение о том, что его кеш относительно измененной переменной устарел, оно не сразу обновляет у себя в кеше это значение а помещает/может поместить его в специальную очередь под названием invalidate queue, которую возможно разгребет чуть позже так как сейчас у этого ядра есть более важные дела. При этом ядро изменившее значение переменной тоже не сразу кладет свои данные в свой кеш. Что бы положить эти данные в свой кеш это ядро должно убедиться в том, что все ядра которым он отправил сообщение об инвалидации своих значений успешно приняли это сообщение. Что бы не простаивать в ожидании подтверждения от других ядер что они получили его сообщение, записавшее значение ядро кладет его в свою очередь на запись в кеш, которая называется store buffer.

Чтение потоками (ядрами) данных
Возвращаемся к примеру
C++
1
2
3
4
5
void add()
{
    data = 100;
    ready = true;
}
Изначально я предполагал, что ready = true; гарантировано будет записано в память после data = 100;
Но тут возникает вопрос. О какой памяти вообще идет речь? А речь идет именно о кеше читающего эти данные потока(ядра), то есть:
C++
1
2
3
if (ready) {
    do something with data
}
Так вот. Это действительно может быть именно так и без всяких барьеров, в зависимоти от гарантий которые предоставляет процессор в отношении своей модели памяти.
Но если процессор таких гарантий не предоставляет, программисту нужно позаботиться об этом самостоятельно.
Почему же на самом деле читающий данные поток может не иметь в наличи data не смотря на то, что ready все таки true?
Как уже упомяналось выше, может возникнуть ситуация когда читающий поток получит сообщение на инвалидацию данных переменной data, но применит эти изменения к своему кешу чуть позже.
На момент чтения data, это "чуть позже" может еще не наступить, и у читающего потока на руках будут старые данные а не свежие, которые появятся после выполнения invalidate queue для синхронизации кешей процессоров.

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

Например, для того, что бы гарантировано знать, что при выполнении ready = true;, данные data = 100; уже сидят в кеш линии текущего ядра, нужно применить барьер StoreStore который гарантирует следующее:
C++
1
2
3
4
5
6
void add()
{
    data = 100;
    StoreStoreBarrier();//Гарантирует, что все операции записи которые идут ДО барьера, будут выполнены ДО всех операций записи которые идут ПОСЛЕ барьера
    ready = true;
}
Читающий поток в свою очередь должен применть LoadLoad барьер который гарантирует, что все операции чтения до барьера будут сделаны ДО всех операций чтения ПОСЛЕ барьера. То есть читающий псевдокод должен быть таким:
C++
1
2
3
4
if (ready) {
    LoadLoadBarrier();  
    do something with data
}
Это приведет к выполнению изменений которые находятся в Invalidate queue читающего потока и таким образом читающий поток запишет себе в кеш данные которые изменил пишуший поток.
В итоге мы получаем такую вот согласованность. Эти барьеры применяются только к тому ядру, на котором выполняется код. Соответственно под каждый код - свои барьеры, если они нужны.
Некоторые детали были опущены специально, что бы упростить изложение этой весьма непростой темы.

Добавлено через 19 минут
...
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
25.02.2017, 15:15
Цитата Сообщение от sys_beginner Посмотреть сообщение
Решить данную проблему можно и без таких классов, забегая вперед отмечу что для решения этой проблемы используются барьеры памяти, а все классы которые дают гарантии определенного "ордеринга" лишь реализуют эти барьеры
Разумеется, класс является не более чем системо-независимой обёрткой над системо-зависимой реализацией. GNU'шники то же самое делали в Си в виде builtin'ов https://gcc.gnu.org/onlinedocs... ltins.html, а в Си++ эти самые обёртки реализовали через builtin'ы. Всё это ради того, чтобы не нужно было возиться с написанием, грубо говоря, ассемблерных вставок под каждую платформу

Цитата Сообщение от sys_beginner Посмотреть сообщение
Семантика relaxed о которой ты говорил как правило не обеспечивает вообще никаких гарантий последовательности доступа к данным
Да, там я ошибся (я потом отписал правильный вариант). Она кроме атомарности вроде бы как не обеспечивает вообще ничего

Цитата Сообщение от sys_beginner Посмотреть сообщение
Например, для того, что бы гарантировано знать, что при выполнении ready = true;, данные data = 100; уже сидят в кеш линии текущего ядра, нужно применить барьер StoreStore который гарантирует следующее:
Такое же свойство включает в себя семантика memory_order_release в классе атомарных операций. Я не знаю, что такое StoreStoreBarrier, но если это какой-то интерфейс, то он явно непереносимый. В то время как var.store(value,std::memory_order_releas e) - это переносимая реализация
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
25.02.2017, 15:19  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
Я не знаю, что такое StoreStoreBarrier, но если это какой-то интерфейс, то он явно непереносимый.
Это был просто псевдокод с пояснением что должен уметь делать барьер и где он должен находится что бы получилось то что нам нужно. Если говорить о С++, что бы применить тот или иной барьер вызывается std::atomic_thread_fence и туда передается уже нужный нам барьер, один из enum-а std::memory_order_*
0
Неэпический
 Аватар для Croessmah
18149 / 10731 / 2067
Регистрация: 27.09.2012
Сообщений: 27,038
Записей в блоге: 1
25.02.2017, 15:23
Цитата Сообщение от Evg Посмотреть сообщение
Я не знаю, что такое StoreStoreBarrier, но если это какой-то интерфейс, то он явно непереносимый. В то время как var.store(value,std::memory_order_releas e) - это переносимая реализация
Ну так есть переносимые барьеры в C++ - std::atomic_thread_fence(/*memory order*/)
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
25.02.2017, 15:56
std::atomic_thread_fence - это слишком консервативное решение, которое надёжно "барьеризует" всё, что можно. А вот запись с std::memory_order_release - это частный случай, который барьеризует только то, что реально надо. И этот вариант работает быстрее, чем std::atomic_thread_fence. Но при этом программист должен понимать, что он делает (т.е. чётко понимать значение всех семантик и как они работают совместно друг с другом). В посте #37 приведён один из вариантов использования. Когда атомарная переменная используется в качестве семафора для критической секции, то запись значения "занято" нужно делать с семантикой "acquire", а запись значения "свободно" - с семантикой "release". Собственно при такой работе мы и получим нужные синхронизации кэшей. Конечно же всё это можно сделать и через std::atomic_thread_fence, но в общем случае это будет работать более медленно (но конкретно на intel'е разницы скорее всего не будет)

Добавлено через 2 минуты
Хотя я всё это описывал из общих соображений, никак не связанных конкретно с классом std::atomic. Я никогда с этими классами не работал, просто предполагаю, что они должны работать именно так (было бы совсем нелогично, если бы они работали как-то по другому)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
25.02.2017, 15:56

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

Запись и считывание разделяемой памяти
Всем доброго времени суток. Столкнулся с одной проблемой. Есть две программы: 1 создаёт разделяемую память и записывает туда данные, а...

Хранение указателей в разделяемой памяти
Выручайте ребята. Задали лабораторную на взаимодействие процессов. Не могу справиться с сохранением указателей на структуры в...

Считать структуру из разделяемой памяти
Добрый день! Есть разделяемый файл. В приложении А в файл запихиваю структуру. В приложении Б считываю эту структуру из файла. Выдается...

Сделать массив из 10 int в разделяемой памяти
Хочу сделать массив из 10 int в разделяемой памяти. Доступ из разных процессов от fork(). Так понимаю, 19-20 строки должны сказать...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
74
Ответ Создать тему
Новые блоги и статьи
Сукцессия 20. Война автоботов. Делаем ставки. Лёд против "пш-пш!".
anaschu 11.07.2026
Битва ИИ в ризосфере: Как мы скрестили жесткие ОДУ, закон Фика и «подземный шантаж» Уважаемые коллеги! Хочу поделиться уникальным опытом ведения междисциплинарной компьютерной разработки. Прямо. . .
Сукцессия 19. Какие выводы я сделал из модели? Часть 2.Внутримикоризный стехиометрический буфер и хроно-дискретные зоны экспансии
anaschu 11.07.2026
Био-Инженерный Ликбез: Архитектура Модели Ризосферы Математический аппарат моделирования
Сукцессия 18. Какие выводы я сделал из войны грибов с деревьями? Часть 1- рассказ и немного про термины
anaschu 11.07.2026
Био-Инженерный Ликбез: Архитектура Модели Ризосферы Математический аппарат моделирования Система ОДУ описывает динамику изменения массы различных компонентов системы во времени. Ключевой. . .
Просветление в 5 секунд
kumehtar 10.07.2026
Андрей Веретенников выдал фразу недавно: "Вам не обязательно кем-то себя считать"
Эксперимент с картинкой-обложкой на CodinGame
Adler 10.07.2026
Решил на днях провести эксперимент. У меня есть аккаунт на CodinGame, ТОП-3 по РФ среди 2200 участников (в разделе соревнования по программированию ИИ-ботов). Там есть обложка, куда можно влепить. . .
GitDOS: Когда я решил скрестить MS-DOS, GitHub и Claude
MaGz GoLd 08.07.2026
Привет, Киберфорум! Хочу поделиться своим проектом, который, надеюсь, вас заинтересует. GitDOS — это веб-приложение, которое позволяет запускать DOS в браузере с тремя крутыми фичами: диски живут в. . .
Учебный мини "цифровой двойник" = (Сервер = Java + Flask_питон )+(3D = Гугловскаая 4T nvideo + google colab) +( WMS = Odoo на Docker)
anaschu 07.07.2026
Увидел в требованиях к вакансии создателя имитационных моделей связку (REst + Api + WMS + Anylogic + питон + 3D Nvideo), и решил посмотреть, что это такое. Из этого всего я работал более менее. . .
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru