На платформе x86 виртуальная машина Hotspot реализует согласованность памяти изменчивых переменных путем добавления блокировки префиксная инструкция.
Например: lock addl $0x0,(%esp);
Руководство разработчика программного обеспечения для архитектуры Intel® 64 и IA-32:
Для процессоров Intel486 и Pentium сигнал LOCK# всегда
подается на шину во время операции LOCK, даже если область блокируемая память кэшируется в процессоре.
Для семейств процессоров P6 и более поздних версий, если область памяти
блокируемая во время операции LOCK, кэшируется в процессор, который
выполняет операцию LOCK в качестве памяти с обратной записью и
полностью содержится в строке кэша, процессор не может выдавать сигнал
LOCK# на шине. Вместо этого он изменит местоположение памяти
внутренне и позволит механизму согласованности кэша гарантировать, что
операция выполняется атомарно. Эта операция называется «блокировкой кэша». «Механизм когерентности кэша автоматически предотвращает
одновременное изменение данных в этой области двумя или более процессорами, которые кэшируют одну и ту же область памяти.
Я думаю, что «если область памяти, блокируемая во время операции LOCK, кэшируется в процессоре», это означает, что состояние строки кэша равно S、E или M (может быть E или M?)。
Прочитал много информации, и говорят, что блокировка кеша реализована через протокол MESI.
Протокол MESI я прочитал, но у меня до сих пор много сомнений, что Я не смог решить.
Во-первых, может ли MESI реализовать блокировку кэша? Во-вторых, как инструкция блокировки обеспечивает согласованность памяти?
Код: Выделить всё
// volatile write
if (cache->is_volatile()) {
//
if (tos_type == itos) {
obj->release_int_field_put(field_offset, STACK_INT(-1));
} else if (tos_type == atos) {
VERIFY_OOP(STACK_OBJECT(-1));
obj->release_obj_field_put(field_offset, STACK_OBJECT(-1));
} else if (tos_type == btos) {
obj->release_byte_field_put(field_offset, STACK_INT(-1));
} else if (tos_type == ztos) {
int bool_field = STACK_INT(-1); // only store LSB
obj->release_byte_field_put(field_offset, (bool_field & 1));
} else if (tos_type == ltos) {
obj->release_long_field_put(field_offset, STACK_LONG(-1));
} else if (tos_type == ctos) {
obj->release_char_field_put(field_offset, STACK_INT(-1));
} else if (tos_type == stos) {
obj->release_short_field_put(field_offset, STACK_INT(-1));
} else if (tos_type == ftos) {
obj->release_float_field_put(field_offset, STACK_FLOAT(-1));
} else {
obj->release_double_field_put(field_offset, STACK_DOUBLE(-1));
}
// after volatile write,insert storeload memory barrier
OrderAccess::storeload();
}
================================
inline void OrderAccess::storeload() {
fence();
}
================================
inline void OrderAccess::fence() {
if (os::is_MP()) {
// always use locked addl since mfence is sometimes expensive
#ifdef AMD64
__asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
#else
__asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
#endif
}
}
[img]https://i.stack. imgur.com/G0whj.png[/img]
Я думаю, что «release_int_field_put» означает запись значения в буфер хранилища?
Итак, что означает «< strong>asm Volatly ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");" сделать, чтобы обеспечить согласованность памяти?
Предположим, есть энергозависимая переменная, которая была загружена в кеш тремя процессорами: CPU1, CPU2, CPU3, теперь все их состояния строки кэша S。< /p>

Согласно в протокол MESI, CPU3 начинает записывать изменчивую переменную. Поскольку статус линии кэша CPU3 равен S, поэтому при записи модификации в буфер Store он также отправляет сообщение Invalidate на адресную шину, чтобы сделать недействительными строки кэша других процессоров. После получения сообщения Invalidate CPU1 и CPU2 отправляют Invalidate ACK, а затем меняют статус своей строки кэша на I. В нормальных обстоятельствах, после того как CPU3 получает подтверждения от всех остальных процессоров, он меняет статус своей строки кэша на E, а затем сбрасывает его. сохранить буфер в линию кэша и, наконец, изменить статус строки кэша на M. В этот момент, если CPU1 инициирует локальное чтение, поскольку его статус строки кэша недействителен, он инициирует запрос чтения на шину, и шина передает запрос на чтение. После того, как CPU3 получает запрос на чтение, он обнаруживает, что его статус строки кэша — M. Согласно протоколу MESI, его необходимо сначала записать обратно в память, а затем CPU1 считывает из памяти. Это гарантирует, что CPU1 прочитает самые последние данные.
Случится ли следующая описанная ситуация?
Если CPU1 уже отправил ACK, но CPU2 занят и не отправил ACK , CPU3 не будет сбрасывать буфер хранения в строку кэша до тех пор, пока не получит подтверждения от всех остальных процессоров. Таким образом, буфер хранения в это время содержит новое значение, но состояние строки кэша CPU3 по-прежнему равно S, и старое значение все еще внутри. Аналогично, память все еще хранит старое значение. Однако в это время CPU1 начинает локальное чтение, обнаруживает, что его состояние линии кэша равно I, и поэтому инициирует запрос чтения на шину. Поскольку в это время строка кэша CPU3 все еще находится в состоянии S, старое значение наконец получается из памяти. Вот почему MESI не может гарантировать согласованность памяти после добавления Store Buffer.
Вышеописанный процесс показан ниже:
ЦП1 читает A
[img]https://i.stack.imgur .com/jCWhy.png[/img]
ЦП3 читает A

ЦП 3 изменяет содержимое A в регистре и записывает его в буфер хранилища.

Должен быть механизм, гарантирующий, что когда CPU3 записывает в строку кэша, другие процессоры не смогут читать или записывать в эту строку кэша, чтобы ЦП мог выполнить полную запись, запись значение в Cache, а затем измените состояние строки Cache Line на M. Так кто же предоставляет этот механизм? Протокол МЭСИ? Насколько я понимаю, MESI не предоставляет такого механизма. Инструкция по блокировке префикса? Но из документации Intel, если он был закеширован, то будет блокироваться не шина, а кэш, и эта блокировка кэша, по мнению некоторых, реализована MESI. Поэтому я в замешательстве.
Я знаю, что для отключения переупорядочения требуется барьер памяти, но мне интересно, не является ли пара блокировок для согласованности кэша фактически нулевой операцией, когда нет нужно отправить сигнал блокировки # на шину? Блокировка реализована посредством арбитража шины?
Подробнее здесь: https://stackoverflow.com/questions/781 ... ory-consis