Есть ли способ сделать функцию атомарной в C? - PullRequest
6 голосов
/ 01 апреля 2009

Есть ли способ сделать функцию атомарной в C.

Я не ищу портативное решение (платформы ищут - Win, Linux)

Ответы [ 7 ]

14 голосов
/ 01 апреля 2009

Может быть.

Это полностью зависит от вашего определения «атомного».

  • В одноядерном, глубоко внедренном окружении без операционной системы вы обычно можете отключать и включать прерывания. Это можно использовать, чтобы позволить функции быть атомарной по отношению к коду обработчика прерываний. Но если у вас есть шина с несколькими основными устройствами, механизм DMA или какое-либо другое аппаратное устройство, которое может записывать память независимо, то даже маскирование прерываний может не дать достаточно надежной гарантии в некоторых обстоятельствах.

  • В среде RTOS (операционной системы реального времени) ядро ​​ОС обычно предоставляет низкоуровневые примитивы синхронизации, такие как критические секции. Критическая секция - это блок кода, который ведет себя «по существу» атомарно, по крайней мере, по отношению ко всем остальным критическим секциям. Обычно это является фундаментальным для реализации ОС других примитивов синхронизации.

  • В многоядерной среде часто доступен низкоуровневый примитив, называемый спин-блокировкой. Он используется для защиты от входа в блок кода, который должен быть атомарным по отношению к другим пользователям того же объекта спин-блокировки, и работает, блокируя ожидающее ядро ​​ЦП в тесной петле, пока не будет снята блокировка (отсюда и название).

  • Во многих потоковых средах более сложные примитивы, такие как события, семафоры, мьютексы и очереди, предоставляются структурой потоков. Они взаимодействуют с планировщиком потоков, так что потоки, ожидающие чего-либо, вообще не работают, пока не будет выполнено условие. Их можно использовать для того, чтобы сделать функции функции атомарными по отношению к другим потокам, совместно использующим тот же объект синхронизации.

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

3 голосов
/ 01 апреля 2009

Если вы хотите убедиться, что ваша функция не будет прервана сигналом, используйте sigprocmask() для маскировки и снятия маски сигналов, хотя некоторые сигналы не могут быть заблокированы (например, SIGKILL) и поведение для блокировки некоторых сигналов (например, SIGSEGV) не определено.

Подробнее см. man sigprocmask.

1 голос
/ 01 апреля 2009

Если под атомарным подразумевается «только один поток за раз», то вы можете просто защитить функцию с помощью блока критического сечения (в Windows). В Linux я использую блокировку / разблокировку мьютекса, чтобы более или менее эмулировать критическую секцию.

1 голос
/ 01 апреля 2009

Определите, что вы подразумеваете под «атомным». Вы имеете в виду «атомарный» в том смысле, что никакой другой процесс или поток не будет выбран для планирования при запуске вашей функции? Или вы имеете в виду, что любые общие объекты, на которые есть ссылки в вашей функции, не будут изменены никакими другими потоками во время выполнения вашей функции?

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

Здесь возникает вопрос: почему вы хотите гарантировать атомарность? В целом, требование о том, что может выполняться только ваш процесс, а не другие, не должно существовать в пространстве пользователя. Если вы хотите убедиться, что определенные структуры данных доступны только одному потоку за раз, вам следует использовать переносную библиотеку потоков (например, pthread) и отгородить свою функцию от критической секции.

1 голос
/ 01 апреля 2009

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

1 голос
/ 01 апреля 2009

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

0 голосов
/ 18 мая 2009

Возможно, вы захотите взглянуть на семафоры POSIX, мьютексы и т. П., Которые могут работать как в Windows, так и в Linux.

Используя, например, В cygwin или minGW можно даже написать переносимый код между Linux и Windows.

Еще лучше вы можете создавать Windows-библиотеки для Linux: http://cdtdoug.blogspot.com/2009/05/mingw-cross-for-linux.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...