2015-07-18 7 views
0

Когда я читаю ldd3, я понимаю концепцию барьера памяти, говорят, что выполнение кода будет переупорядочено по той причине, что оптимизация кеширования и компиляции. Я думаю, что коды, которые не имеют зависимостей, могут быть переупорядочены для получения лучшего результата, а регистры портов ввода-вывода не могут быть оптимизированы, поскольку в нем должны содержаться согласованные данные. Но я не могу понять этот код ниже, и есть ли какие-то правила, чтобы следовать, куда мне следует вставлять функции, такие как smb(), mb(), барьер()?Как я могу судить, где я должен помещать барьер памяти в код?

Например, в примере код short от ldd3.

/* 
* Atomicly increment an index into short_buffer 
*/ 
static inline void short_incr_bp(volatile unsigned long *index, int delta) 
{ 
    unsigned long new = *index + delta; 
    barrier(); /* Don't optimize these two together */ 
    *index = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new; 
} 

Как линия перед барьером и линия после переустановки барьера? Я думаю, что последнее зависит от того, чтобы первое было выполнено первым, чтобы получить новое значение. Это действительно меня смущает.

+1

Мой ответ в http://stackoverflow.com/questions/20446982/determining-the-location-for-theage-of-barriers-fences/20476292 описывает некоторые общие закономерности, на которые следует обратить внимание. Потому что они требуют памяти барьеры. –

ответ

0

Заслонки памяти используются вместо блокировки для лучшего улучшения производительности. Существует несколько стандартных паттернов, когда барьеры памяти обеспечивают необходимый синхронизатор. Вы можете прочитать, например, Documentation/memory-barriers.txt из источников ядра.

В данном примере использование барьеров ldd3 является более сложным, чем обычно. В терминах текущего ядра (в отличие от 2.20+, описанного в ldd3) одно и то же намерение может быть выражено с использованием макроса ACCESS_ONCE().

unsigned long new = *index + delta; 
ACCESS_ONCE(*index) = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new; 

Без ПРЕГРАД, компилятор может назначить * индекс дважды:

*index = *index + delta; 
if(*index > (short_buffer + PAGE_BUFFER) 
    *index = short_buffer; 

Поскольку *index используется в нескольких потоках, как незащищенными инвариантно (это показывает, какой буфер область доступна), написание промежуточного значение *index + delta в него сделать инвариантным, видно из другого потока, неверно. Это предотвращается макросом ACCESS_ONCE(), который заставляет компилятор генерировать доступ (запись) к переменной только тогда, когда запрос явно запрошен.

Фактически, ACCESS_ONCE (и барьер в вашем коде) является redudant для переменной с модификатором volatile.