Общая память может быть считана и проверена без взаимных исключений?

На Linux я использую shmget и shmat устанавливать сегмент общей памяти, который один процесс запишет в и один или несколько процессов, будет читать из. Данные, которые совместно используются, являются несколькими мегабайтами в размере и при обновлении полностью переписывается; это частично никогда не обновляется.

Мне разметили мой сегмент общей памяти следующим образом:

    -------------------------
    | t0 | actual data | t1 |
    -------------------------

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

Эта процедура гарантирует, что, если читатель думает, данные допустимы затем, это на самом деле?

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

6
задан Bribles 1 April 2010 в 16:51
поделиться

2 ответа

Джо Даффи дает точно такой же алгоритм и называет его: «Масштабируемая схема чтения / записи с оптимистической повторной попыткой» .

Это работает.
Вам нужно два поля порядковых номеров.

Их нужно читать и писать в обратном порядке.
Возможно, вам потребуется установить барьеры памяти, в зависимости от гарантий упорядочения памяти в системе .

В частности, вам нужна семантика чтения, получения и сохранения, для считывающих и записывающих, когда они обращаются к t 0 или t 1 для чтения и записи соответственно.

Какие инструкции необходимы для этого, зависит от архитектуры. Например. на x86 / x64 из-за относительно сильных гарантий в данном конкретном случае не нужны никакие специфические для машины барьеры * .

* по-прежнему необходимо убедиться, что компилятор / JIT не возится с загрузками и сохранениями, например с помощью volatile (который имеет другое значение в Java и C #, чем в ISO C / C ++. Однако компиляторы могут отличаться. Например, при использовании VC ++ 2005 или более поздней версии с volatile, было бы безопасно сделать это. См. «Специфичный для Microsoft» раздел Это может быть выполнено с другими компиляторами, а также на x86 / x64.Выпущенный ассемблерный код должен быть проверен, и нужно убедиться, что доступ к t 0 и t 1 не удаляется или не перемещается компилятором.)

В качестве примечания. , если вам когда-нибудь понадобится MFENCE , lock или [TopOfStack], 0 может быть лучшим вариантом, в зависимости от ваших потребностей .

2
ответ дан 17 December 2019 в 00:06
поделиться

Современное оборудование на самом деле совсем не согласованно. Таким образом, не гарантируется, что это будет работать как таковое, если вы не выполните барьеры памяти в соответствующих местах. Барьеры необходимы, потому что архитектура реализует более слабую модель согласованности разделяемой памяти, чем последовательная согласованность. Само по себе это не имеет ничего общего с конвейерной обработкой или OoO, но позволяет нескольким процессорам эффективно обращаться к системе памяти параллельно. См., Например, Модели согласованности совместно используемой памяти: Учебное пособие . На однопроцессоре вам не нужны барьеры, потому что весь код выполняется последовательно на этом одном процессоре.

Кроме того, нет необходимости иметь два поля времени, счетчик последовательности, вероятно, является лучшим выбором, поскольку нет необходимости беспокоиться о том, что два обновления настолько близки, что получают одинаковую временную метку, а обновление счетчика происходит намного быстрее. чем получить текущее время. Кроме того, нет никаких шансов, что часы переместятся назад во времени, что могло бы произойти, например, когда ntpd настраивается на смещение часов. Хотя эту последнюю проблему можно решить в Linux, используя clock_gettime (CLOCK_MONOTONIC, ...). Еще одно преимущество использования счетчиков последовательностей вместо меток времени состоит в том, что вам нужен только один счетчик последовательностей. Писатель увеличивает счетчик как перед записью данных, так и после завершения записи. Затем считыватель считывает порядковый номер, проверяет его четность и, если да, считывает данные и, наконец, затем снова считывает порядковый номер и сравнивает его с первым порядковым номером. Если порядковый номер нечетный, это означает, что идет запись и нет необходимости читать данные.

Ядро Linux использует блокирующий примитив, называемый seqlocks , который выполняет что-то подобное вышеупомянутому. Если вы не боитесь «заражения GPL», вы можете найти реализацию в Google; По сути, это тривиально, но весь фокус в том, чтобы установить правильные барьеры.

5
ответ дан 17 December 2019 в 00:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: