О гарантии атомарности в C

На машинах x86 такие инструкции, как inc, addl, не являются атомарными, и в среде SMP их небезопасно использовать без префикса блокировки. Но в среде UP это безопасно, так как inc, addl и другие простые инструкции не будут прерваны.

Моя проблема в том, что, учитывая оператор уровня C, например

x = x + 1;

Есть ли какие-либо гарантии, что компилятор C всегда будет использовать UP-безопасные инструкции, такие как

incl %eax

но не те небезопасные для потоков инструкции (например, реализация оператора C в нескольких инструкциях, которые могут быть прерваны переключением контекста) даже в среде UP?


person ZelluX    schedule 18.09.2009    source источник


Ответы (8)


Нет абсолютно нет гарантии, что "x - x + 1" будет компилироваться в инструкции, безопасные для прерываний, на любой платформе, включая x86. Вполне может быть, что это безопасно для конкретного компилятора и конкретной архитектуры процессора, но это вообще не обязательно в стандартах, и стандарт - единственная гарантия, которую вы получаете.

Вы не можете считать что-либо безопасным, основываясь на том, во что, по вашему мнению, оно будет скомпилировано. Даже если конкретный компилятор/архитектура утверждает, что это так, полагаться на него очень плохо, поскольку это снижает переносимость. Другие компиляторы, архитектуры или даже более поздние версии того же компилятора и архитектуры могут довольно легко сломать ваш код.

Вполне возможно, что x = x + 1 может скомпилироваться в произвольную последовательность, например:

load r0,[x]  ; load memory into reg 0
incr r0      ; increment reg 0
stor [x],r0  ; store reg 0 back to memory

на ЦП, который не имеет инструкций по увеличению памяти. Или он может быть умным и скомпилировать его в:

lock         ; disable task switching (interrupts)
load r0,[x]  ; load memory into reg 0
incr r0      ; increment reg 0
stor [x],r0  ; store reg 0 back to memory
unlock       ; enable task switching (interrupts)

где lock отключает, а unlock разрешает прерывания. Но даже в этом случае это может быть не потокобезопасным в архитектуре, в которой более одного из этих ЦП совместно используют память (lock может отключать прерывания только для одного ЦП), как вы уже заявили.

Сам язык (или библиотеки для него, если он не встроен в язык) предоставит потокобезопасные конструкции, и вам следует использовать их, а не зависеть от вашего понимания (или, возможно, непонимания) того, какой машинный код будет сгенерирован.

Такие вещи, как Java synchronized и pthread_mutex_lock() (доступные для C под некоторыми ОС), - это то, что вам нужно изучить.

person paxdiablo    schedule 18.09.2009

No.

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

Чтобы быть в безопасности, вы должны либо использовать встроенный ассемблер, либо, если вам нужно оставаться переносимым, инкапсулировать приращение с помощью мьютексов.

person Gunther Piez    schedule 18.09.2009

Если вы используете GLib, у них есть макросы для атомарных операций с целыми числами и указателями.

http://library.gnome.org/devel/glib/stable/glib-Atomic-Operations.html

person u0b34a0f6ae    schedule 18.09.2009

В последних версиях GCC есть встроенные функции __sync_xxx, позволяющие делать именно то, что вы хотите.

Вместо того, чтобы писать:

x += 1;

напишите это:

__sync_fetch_and_add(&x, 1);

И gcc позаботится о том, чтобы это было скомпилировано в атомарный код операции. Теперь это поддерживается на большинстве важных арок.

http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

Первоначально он возник из рекомендаций Intel для C на ia64, но теперь нашел способы использовать gcc и на многих других арках. Так что это даже немного портативно.

person user175104    schedule 18.09.2009

Есть ли гарантии, что компилятор C всегда будет использовать UP-безопасные инструкции?

Не в стандарте C. Но ваш компилятор/стандартная библиотека может предоставить вам специальные типы или определенные гарантии.

Этот документ gcc может соответствовать тому, что тебе нужно.

person Artelius    schedule 18.09.2009

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

person jldupont    schedule 18.09.2009

Компилятор C может реализовать такую ​​инструкцию, как x = x + 1, в нескольких инструкциях.
Вы можете использовать ссылку register указывает компилятору использовать регистр вместо памяти, но компилятор волен игнорировать его.

Я предлагаю использовать специальные процедуры блокировки ОС, такие как функция InterlockedIncrement в Windows.

person Nick Dandoulakis    schedule 18.09.2009
comment
Заботился ли какой-нибудь компилятор за последние 10 или 15 лет о ключевом слове register? - person erikkallen; 18.09.2009
comment
@erikkallen, может быть, нет. "Регистровые" оптимизации в любом случае выполняются компиляторами, поэтому и нужна "изменчивая". - person Nick Dandoulakis; 18.09.2009

Беспокойство только о x86 в любом случае ужасно непереносимое кодирование. Это одна из тех, казалось бы, небольших задач по кодированию, которая сама по себе оказывается проектом. Найдите существующий библиотечный проект, который решает подобные проблемы для широкого круга платформ, и используйте его. Судя по тому, что говорит kaizer.se, GLib один из них.

person Lee B    schedule 18.09.2009