Является ли 8-байтовый memcpy() атомарным на 64-битной машине с Linux?

Я использую 8-байтовый сегмент общей памяти в PHP, используя функции shmop_*. Глядя на исходный код PHP, я вижу, что функции shmop_write() и shmop_read() используют memcpy() для заполнения/чтения этих 8 байт.

Интересно, достаточно ли на 64-битной Linux-машине memcpy() умен, чтобы скопировать все двойное слово за один раз (одну инструкцию), что делает операции чтения и записи эффективными атомарными.

Я не уверен, что эти сегменты общей памяти всегда выровнены по 64-битному принципу.

В качестве примера:

$shmop = shmop_open(ftok(__FILE__, 'R'), 'c', 0644, 8);

shmop_write($shmop, "abcdefgh", 0); // <= is this operation atomic
$a = shmop_read($shmop, 0, 8); // <= is this operation atomic

Я не уверен, что есть основания ожидать, что строка "abcdefgh" будет правильно выровнена.

Barmar 15.08.2024 18:15

Разве API модуля не синхронизирован извне?

dimich 16.08.2024 09:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если memcpy, о котором вы говорите, это функция C, сборка glibc x86-64 memcpy for size=8 разветвляется на случай 8-15, который создает две полностью перекрывающиеся 8-байтовые копии. (https://codebrowser.dev/glibc/glibc/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S.html#308).

Для больших размеров до 15 они будут перекрываться посередине на меньшее количество байтов. Так что shmop_write могло случиться как до, так и после очередного магазина. Для shmop_read имеет значение только второе прочтение; результат первого перезаписывается, поэтому он фактически похож на C++ std::atomic .load(acquire) на x86-64.

Это деталь реализации, которая ничем не гарантирована. Однако выбор имеет смысл: если бы они использовали диапазон size=5-8, который либо частично перекрывался, либо не перекрывался вообще для двух 4-байтовых копий, 8-байтовая перезагрузка результата привела бы к остановке пересылки в хранилище, поскольку он перекрывается. два отдельных магазина.


Я не знаю, что делает MUSL, другие библиотеки C или другие архитектуры.

Другие архитектуры с эффективными невыровненными загрузками/сохранениями (например, AArch64) могут делать что-то подобное для размеров, меньших 2x целочисленного регистра. Но это не гарантировано. Если бы он использовал эту стратегию, это было бы похоже на .load(relaxed), поскольку AArch64 слабо упорядочен.

Это, конечно, предполагает, что общая память достаточно выровнена, чтобы простые загрузки/сохранения были атомарными на целевой машине. (Локальная переменная, из/в которую вы копируете, не имеет значения, поскольку она не является общей. Итак, как отметил Бармар в комментариях, "abcdefgh" может быть не выровнена, но это не имеет значения, потому что вам не нужен атомарный доступ к ней. , только для хранения этих 8 байт из регистра, как только они туда попадут.)

В процессорах Intel это означает, что 8 байт общей памяти не должны охватывать границу строки кэша, но могут быть смещены в любом месте внутри 64-байтового блока. В AMD он должен быть выровнен по 8 байтам. (На более поздних процессорах AMD смещенное выравнивание в пределах 16-, 32- или даже 64-байтового блока по-прежнему будет давать атомарные загрузки/сохранения.) См. Почему целочисленное присваивание естественно выровненной переменной является атомарным на x86?

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

Спасибо за длинный и ясный ответ. Если я правильно понимаю, это код glibc(), на который вы ссылаетесь: ` movq -8(%rsi, %rdx), %rcx movq (%rsi), %rsi movq %rsi, (%rdi) movq %rcx, - 8(%rdi, %rdx)`, который эффективно (с rdx=8) копирует 2 раза одни и те же 8 байтов из/в одни и те же места, гарантируя атомарность. Итак, единственный оставшийся вопрос: «выровнены ли общие сегменты по словам». Просматривая исходный код shmop_open(), он вызывает shmget(), а затем shmat(), чтобы получить адрес. На странице руководства shmat() говорится, что она возвращает адреса, выровненные по страницам. Это означает, что общая память всегда выравнивается по словам, не так ли?

Stephen Cantini 18.08.2024 17:50

@StephenCantini: Да, это ассемблер для x86-64 glibc. Обратите внимание, что он выполняет два хранилища, поэтому два memcpy из двух потоков могут позволить наблюдателю увидеть четыре изменения, а не только два. Но да, если ваша общая память всегда является началом региона shmem, тогда она должна быть выровнена по страницам и, следовательно, по 8 байтам. Если бы чей-то реальный вариант использования использовал какое-то смещение в области shmem, то ему пришлось бы беспокоиться о том, что оно кратно 8.

Peter Cordes 18.08.2024 22:00

Другие вопросы по теме