Зачем нам нужны барьеры для чтения и записи? - PullRequest
2 голосов
/ 19 апреля 2020

Зачем нам нужно определять два типа барьеров с одинаковой реализацией?

Например, этот код из io_uring в Linux:

#if defined(__x86_64) || defined(__i386__)
#define read_barrier()  __asm__ __volatile__("":::"memory")
#define write_barrier() __asm__ __volatile__("":::"memory")
#else

Ответы [ 2 ]

4 голосов
/ 20 апреля 2020

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

Это всего лишь общие c барьеры времени компиляции, сделанные через часть встроенной сборки, которая, если используется, предотвращает переупорядочение команд G CC. Это очень хорошо объяснено в этом другом посте . То, что может быть достигнуто с помощью этого «трюка», обычно также возможно с помощью спецификатора C volatile.

Обратите внимание, что ядро ​​Linux не использует эти указанные макросы c где-либо в коде, это всего лишь два макроса, определенные для io_uring инструментов тестирования пользовательского пространства. Он определенно использует asm volatile ("" ::: "memory"), где это необходимо, но под разными именами (например, smp_rmb(), smp_wmb()).

Модель памяти x86 делает sfence и lfence совершенно бесполезными для связь между процессорами; Достаточно заблокировать переупорядочение во время компиляции: см. Делает ли модель памяти Intel избыточность SFENCE и LFENCE?

smp_mb() является полным барьером и требует фактической инструкции asm, а также блокирование переупорядочения во время компиляции.


x86 действительно имеет некоторые инструкции asm барьера памяти для барьеров памяти только для чтения и только для записи "реальной" (во время выполнения). Это sfence (ограждение магазина), lfence (ограждение загрузки) и mfence (ограждение памяти = полный барьер).

mfence сериализует чтение и запись (полный барьер), в то время как остальные только сериализовать один из двух (читает ИЛИ пишет или загружает ИЛИ сохраняет). Страница википедии , посвященная упорядочению памяти, хорошо объясняет их значение. lfence фактически блокирует переупорядочение LoadStore, а не только LoadLoad, для слабо упорядоченных movntdqa загрузок из памяти W C. Переупорядочение других видов загрузок из других типов памяти уже запрещено, поэтому практически никогда нет оснований использовать lfence для упорядочения памяти вместо другого эффекта блокировки вышедшего из строя exe c.

Ядро использует те фактические инструкции asm для барьеров памяти в коде ввода / вывода, например, mb(), rmb() и wmb() , которые расширяются точно до mfence, lfence, sfence и другие ( пример ).

sfence и lfence в большинстве случаев, вероятно, излишни, например, вокруг MMIO со строго упорядоченной памятью U C. Для записи в память W C может потребоваться защита. Но они не слишком медленные по сравнению с вводом / выводом, и в некоторых случаях могут возникнуть проблемы, поэтому Linux использует безопасный подход.

В дополнение к этому, x86 имеет другой вид барьеров для чтения / записи, которые также могут быть быстрее (например, тот, который я связал выше). См. Следующие ответы для получения дополнительной информации о полных барьерах (то, что C11 называет последовательной согласованностью) с помощью либо mfence, либо фиктивной lock инструкции ed:

2 голосов
/ 19 апреля 2020

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

...