2012-02-15 2 views
13

Недавно я заглянул в реализацию ядра ядра атомарного чтения и записи, и появилось несколько вопросов.Чтение и запись реализации атомной операции в ядре Linux

Сначала соответствующий код из архитектуры ia64:

typedef struct { 
    int counter; 
} atomic_t; 

#define atomic_read(v)  (*(volatile int *)&(v)->counter) 
#define atomic64_read(v) (*(volatile long *)&(v)->counter) 

#define atomic_set(v,i)  (((v)->counter) = (i)) 
#define atomic64_set(v,i) (((v)->counter) = (i)) 
  1. Для обеих операций чтения и записи, кажется, что прямой подход был принят для чтения или записи переменной. Если в другом месте нет другого трюка, я не понимаю, какие гарантии существуют, что эта операция будет атомарной в домене сборки. Я предполагаю, что очевидный ответ будет заключаться в том, что такая операция преобразуется в один код операции сборки, но даже в этом случае, как это гарантируется при учете различных уровней кэша памяти (или других оптимизаций)?

  2. На прочитанных макросах летучий тип используется в трюке для кастинга. Кто-нибудь знает, как это влияет на атомарность здесь? (Обратите внимание, что он не используется в операции записи)

ответ

13

Я думаю, что вы неправильно понимаете (очень неопределенное) использование слова «атомный» и «неустойчивый» здесь. Atomic только на самом деле означает, что слова будут прочитаны или записаны атомарно (в один шаг и гарантируя, что содержимое этой позиции памяти будет всегда писать или другое, а не что-то среднее). И ключевое слово volatile говорит компилятору никогда не принимать данные в этом месте из-за более раннего чтения/записи (в основном, никогда не оптимизировать чтение).

Что означает слова «атомный» и «изменчивый», НЕ означает, что есть какая-либо форма синхронизации памяти. Ни один из них не подразумевает ЛЮБОЙ барьер или ограждения для чтения/записи. Ничто не гарантируется в отношении согласованности памяти и кеша.Эти функции в основном атомные только на уровне программного обеспечения, и аппаратное обеспечение может оптимизировать/лежать, но оно сочтет нужным.

Теперь, почему просто чтение достаточно: модели памяти для каждой архитектуры разные. Многие архитектуры могут гарантировать атомарное чтение или запись для данных, согласованных с определенным смещением байта, или длиной x слов и т. Д. И варьироваться от CPU к CPU. Ядро Linux содержит множество определений для разных архитектур, которые позволяют делать это без каких-либо атомных вызовов (CMPXCHG, в основном) на платформах, которые гарантируют (иногда даже на практике, даже если на самом деле их спецификация говорит, что на самом деле не гарантируют) атомные чтения/пишет.

Что касается volatile, в то время как нет никакой потребности в этом вообще если вы не доступ к памяти, отображенные IO, все это зависит от того, когда/где/почему atomic_read и atomic_write макросов называют. Многие компиляторы будут (хотя он не установлен в спецификации C) генерируют барьеры/ограждения памяти для переменных переменных (GCC, с моей точки зрения, является одним из них. MSVC делает это точно). Хотя это обычно означает, что все читает/записывает эту переменную, теперь официально освобождается от практически любых оптимизаций компилятора, в этом случае путем создания «виртуальной» изменчивой переменной только этот конкретный экземпляр чтения/записи отключен -лимиты для оптимизации и переупорядочения.

2

Если вы пишете для определенной архитектуры, вы можете сделать определенные для нее предположения.
Я предполагаю, что IA-64 скомпилирует эти вещи к одной инструкции.

Кэш не должен быть проблемой, если счетчик не пересекает границу строки кэша. Но если требуется выравнивание 4/8 байт, этого не может быть.

«Настоящая» атомная инструкция требуется, когда машинная инструкция преобразуется в два обращения к памяти. Это относится к инкрементам (чтение, увеличение, запись) или сравнение & swap.

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

3

Чтения являются атомными на большинстве основных архитектур, при условии, что они выровнены по краям их размера (и не превышают размер чтения типа ввода), см. Руководства по архитектуре Intel. С другой стороны, многие записи различны, Intel заявляет, что в x86 однобайтовые записи и выровненные записи могут быть атомарными, в соответствии с IPF (IA64) все используют семантику получения и выпуска, что сделает ее гарантированной атомной, см. this.

volatile не позволяет компилятору кэшировать значение локально, заставляя его извлекать, когда есть доступ к нему.

+0

В современных ядрах 'volatile' здесь используется через макрос, называемый' READ_ONCE() '/' WRITE_ONCE'. Устный перевод в моей голове заключается в том, что компиляторам технически разрешено читать/записывать значение * несколько раз. Например. если код копирует значение чтения в локальную переменную, которая затем используется в разных местах. Поэтому мы должны описать это как нечто большее, чем просто предотвращение кэширования ценности локально. Полное описание: https://lwn.net/Articles/508991/ – sourcejedi

Смежные вопросы