Если i
и j
- локальные переменные, ничего. Компилятор может хранить их в регистрах во время вызова функции, если он может доказать, что ничто за пределами текущей функции не имеет их адреса.
Но любые глобальные переменные или локальные переменные, адрес которых может храниться в глобальном, do должны быть "синхронизированы" в памяти для вызова не встроенной функции. Компилятор должен предполагать, что любой вызов функции, который он не может встроить, изменяет любую / каждую переменную, на которую он может ссылаться.
Так, например, если int i;
- локальная переменная, после sscanf("0", "%d", &i);
ее адрес будет ускользать от функции и компилятор затем должен будет пролить / перезагрузить его вокруг вызовов функций вместо того, чтобы хранить его в регистре с сохранением вызовов.
См. Мой ответ на Понимание изменчивой asm и изменчивой переменной, с пример того, что asm volatile("":::"memory")
является барьером для локальной переменной, адрес которой экранирован функцией (sscanf("0", "%d", &i);
), но не для локальных, которые все еще остаются чисто локальными. Это точно такое же поведение по той же причине.
Я предполагаю, что приведенная выше цитата говорит о переупорядочивании ЦП, а не о переупорядочении компилятора.
Речь идет о обоих, потому что оба необходимы для правильности.
Вот почему компилятор не может изменить порядок обновлений общих переменных с помощью любого вызова функции. (Это очень важно: слабая модель памяти C11 допускает много времени компиляции переупорядочивание. Сильная модель памяти x86 допускает только переупорядочивание StoreLoad и локальную пересылку хранилища.)
pthread_mutex_lock
, будучи не встроенным вызовом функции, заботится о переупорядочении во время компиляции, и тот факт, что он выполняет операцию lock
ed, атомарный RMW, также означает, что он включает полный барьер памяти во время выполнения на x86. (Но не сама инструкция call
, а просто код в теле функции.) Это придает ей семантику.
Для разблокировки спин-блокировки требуется только хранилище релизов, а не RMW, поэтому в зависимости от деталей реализации функция разблокировки может не быть барьером StoreLoad. (Это все еще нормально: он предотвращает выпадение всего в критическом разделе. Нет необходимости останавливать появление последующих операций до разблокировки. См. Джефф Прешинг статья, объясняющая семантику получения и выпуска)
На слабо упорядоченном ISA эти функции мьютекса будут запускать инструкции барьера, такие как ARM dmb
(барьер памяти данных). Обычные функции не работают, поэтому автор этого руководства прав, указывая на то, что эти функции являются особенными.
Теперь, что мешает ЦП переупорядочить mov 10 в i и mov 20 в j, чтобы было выше call pthread_mutex_lock()
Это не важная причина (потому что на ISA со слабым упорядочением pthread_mutex_unlock
будет запускать инструкцию барьера), но на самом деле это правда на x86, что хранилища нельзя даже переупорядочить с помощью инструкции call
не говоря уже о фактической блокировке / разблокировке мьютекса, выполняемой телом функции перед возвратом функции.
x86 имеет строгую семантику упорядочивания памяти (хранилища не меняют порядок с другими хранилищами), а call
- хранилище (отправка адреса возврата).
Таким образом, mov [i], 10
должен появиться в глобальном хранилище между хранилищами, выполняемыми инструкцией call
.
Конечно, в обычной программе никто не наблюдает за стеком вызовов других потоков, только xchg
, чтобы взять мьютекс, или хранилище релизов, чтобы освободить его в pthread_mutex_unlock
.
person
Peter Cordes
schedule
20.06.2018
pthread_mutex_lock()
иpthread_mutex_unlock()
реализуют свои обещания относительно упорядочивания во время выполнения. ЦП, которые выполняют такое переупорядочение, также имеют инструкции для его модуляции, и функции блокировки / разблокировки мьютексов используют их (среди прочего). - person John Bollinger   schedule 20.06.2018call
, как если бы это была любая другая инструкция. Не может. Переупорядочивание происходит против динамической трассировки. - person BeeOnRope   schedule 20.06.2018pthread_mutex_lock/pthread_mutex_unlock
реализует барьер ЦП, но почему эти функции мьютекса обязаны реализовывать указанный барьер ЦП из значения мьютекса. Ниже я дам свой ответ на первый вопрос (Что теперь мешает ...), по-новичку для новичков, ... - person zzzhhh   schedule 12.06.2021pthread_mutex_lock()
- это не что иное, как последовательность инструкций, так что, в общем, опять же, ЦП не подозревает, что это настолько особенная функция, что переупорядочивание памяти процессора запрещено. Итак, вообще говоря, в третий раз ничто не может помешать процессору переупорядочитьmov 10 into i
на вышеcall pthread_mutex_lock()
. В результате возникает естественный вопрос. Теперь позвольте мне ответить ниже, начиная со значения мьютекса. ... - person zzzhhh   schedule 12.06.2021mov 10 into i
переупорядочен вышеcall pthread_mutex_lock()
в потоке 1, поток 2 может запускать критическую секцию в этой точке, потому что поток 1 еще не получил мьютекс, что является нарушением значения мьютекса. Вы можете найти множество примеров сбоев, вызванных запуском кода критической секции двумя потоками одновременно. ... - person zzzhhh   schedule 12.06.2021pthread_mutex_lock()
должен служить барьером для памяти. Конечно, простое наименование функции mutex_lock или что-то в этом роде не означает, что она будет функционировать как получение мьютекса; мы должны это реализовать. Фактически, мы должны реализовать не только традиционное получение мьютексов, о котором написано во многих учебниках, но и семантику получения. Как сказано в следующем абзаце предыдущей статьи, каждая реализация блокировки ... должна обеспечивать эти гарантии. ... - person zzzhhh   schedule 12.06.2021call pthread_mutex_lock()
исключительно из соображений добросовестности и уважения семантики мьютекса. На практике вполне вероятно, что программисту не удалось записать барьер памяти ЦП в функции, но он добавил его позже в качестве исправления ошибки, или случайно использовал некоторые инструкции, которые автоматически реализуют барьер, даже не зная об этом программисту. Эти детали реализации подробно описаны в ответах экспертов, поэтому повторяться не буду. Тот же аргумент можно применить дляpthread_mutex_unlock()
, чтобы соблюсти семантику выпуска. - person zzzhhh   schedule 12.06.2021