Потокобезопасные атомные операции в gcc
В программе, над которой я работаю, у меня много кода:
pthread_mutex_lock( &frame->mutex );
frame->variable = variable;
pthread_mutex_unlock( &frame->mutex );
Это явно пустая трата циклов CPU, если среднюю инструкцию можно просто заменить атомным хранилищем. Я знаю, что gcc вполне способен на это, но я не смог найти много документации по таким простым потокобезопасным атомным операциям. Как заменить этот набор кода на атомную операцию?
(Я знаю, что простые хранилища теоретически должны быть атомарными, но я не хочу надеяться, что оптимизатор не навредит своей атомности в какой-то момент процесса.)
Уточнение: я не нуждаюсь в том, чтобы они были строго атомарными; эти переменные используются исключительно для синхронизации потоков. То есть Thread B считывает значение, проверяет, правильно ли оно, и если оно не правильно, оно спит. Поэтому, даже если Thread A обновляет значение, и Thread B не реализует его обновление, это не проблема, так как это означает, что Thread B спит, когда это действительно не нужно, и когда он просыпается, значение будет быть правильным.
Ответы
Ответ 1
Вы можете проверить документацию gcc. Для текущей версии gcc (4.3.2) это будет глава 5.47 Встроенные функции для доступа к атомной памяти - для других версий gcc проверьте свои Docs. Он должен быть в главе 5 "Расширения семейства языков C".
Кстати, компилятор C абсолютно не гарантирует, что простые операции хранилища являются атомарными. Вы не можете полагаться на это предположение. Для того чтобы машинный код операции выполнялся атомарно, ему нужен префикс LOCK.
Ответ 2
До определенной точки атомные операции в C были предоставлены прямо из источников ядра через заголовок atomic.h.
Однако наличие заголовков ядра, используемых непосредственно в коде пользовательского пространства, является очень плохой практикой, поэтому заголовочный файл atom.h был удален некоторое время назад. Вместо этого мы теперь используем "GCC Atomic Builtins", которые намного лучше и надежнее.
Существует очень хорошее объяснение, предоставленное Тудором Голубенко в его блоге. Он даже обеспечивает замену исходного файла atomic.h, если у вас есть какой-то код, который ему нужен.
К сожалению, я новичок в stackoverflow, поэтому я могу использовать только одну ссылку в своих комментариях, поэтому проверьте сообщение Tudor и получите просветленный.
Ответ 3
В x86 и большинстве других архитектур выровненные 4-байтовые чтения и записи всегда являются атомарными. Оптимизатор может пропустить/изменить порядок чтения и записи в одном потоке.
Что вы хотите сделать, это сообщить компилятору, что другие потоки, возможно, коснулись этой памяти. (Побочный эффект pthread_mutex_lock
говорит компилятору, что другие потоки могли коснуться любой части памяти.) Вы можете увидеть volatile
рекомендованный, но это не в спецификации C, а GCC не интерпретирует volatile
, что путь.
asm("" : "=m" (variable));
frame->variable = variable;
является механизмом, специфичным для GCC, чтобы сказать, что "variable
было записано, перезагрузите его".
Ответ 4
AFAIK, вы не можете префикс команд MOV с помощью LOCK; это разрешено только для операций RMW. Но если он использует простой магазин, ему может также понадобиться барьер памяти, который подразумевается с помощью мьютекса, а также с инструкциями, которые позволяют LOCK.
Ответ 5
Как я могу видеть, вы используете платформу gnu для разработки, поэтому можно с уверенностью сказать, что glic предоставляет тип данных, содержащий атомные возможности, 'sig_atomic_t'
. Таким образом, этот подход может обеспечить вам атомные операции на уровнях ядра. а не gcc.