Как атомарно выделить петлевое устройство?


11

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

Я знаю, что могу использовать losetup -fдля получения следующего нераспределенного устройства цикла, а затем выделить это устройство цикла следующим образом:

ld=$(losetup -f)
sudo losetup $ld myfile.img
dostuffwith $ld

Однако в случае, когда я хочу запустить несколько экземпляров программы одновременно, это почти учебный пример состояния гонки, и это очень беспокоит меня. Если бы у меня было несколько запущенных экземпляров этой программы или другие программы, пытающиеся также получить устройство цикла, то каждый процесс мог бы не иметь возможности выделить устройство цикла до вызова следующего losetup -f, и в этом случае оба процесса думают, что один и тот же цикл устройство доступно, но получить его может только один.

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

Как я могу избежать этого потенциального состояния гонки? В идеале я хотел бы иметь возможность обнаруживать и связывать устройство цикла атомарно, например, с помощью такой команды:

ld=$(sudo losetup -f myfile.img)
dostuffwith $ld

Тем не менее, когда я это делаю, $ldне назначается путь устройства петли, и перемещение sudo, как в, sudo ld=$(losetup -f myfile.img)дает ошибки разрешения.

Ответы:


13

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

Используйте losetupрежим автоматического выделения ( -f) и передайте --showопцию, чтобы он печатал путь устройства петли.

ld=$(sudo losetup --show -f /tmp/1m)

Эта опция присутствует в util-linux начиная с версии 2.13 ( изначально добавлена ​​как-s , но --showподдерживается во всех выпущенных версиях, а в последних версиях -sимя этой опции удалено). К сожалению, версия BusyBox не имеет его.

Версия 3.1 ядра Linux представила метод для выполнения операции выделения циклического устройства непосредственно в ядре через новое /dev/loop-controlустройство. Этот метод поддерживается только начиная с util-linux 2.21. При ядре <3.1 или util-linux <2.21 losetupпрограмма перечисляет записи устройства цикла для их резервирования. Я не вижу условия гонки в коде, хотя; он должен быть безопасным, но у него может быть небольшое окно, во время которого он будет неправильно сообщать, что все устройства распределены, даже если это не так.


Для чего </dev/tty?
Стефан Шазелас

1
В прошлый раз попробовал даже losetup --find --showгонки. for i in {1..100}; do losetup -f -s $i & doneне дал мне 100 устройств петли. Петлевые устройства достаточно редки, чтобы это не имело значения; если это так, единственный вариант - сделать свои собственные блокировки и / или проверить, что правильное устройство петли было создано как запоздалая мысль.
frostschutz

@frostschutz losetupможет завершиться ошибкой (например, потому что у вас закончились циклические записи), но если он сообщает имя устройства, это устройство, которое он успешно выделил. Была ли в более ранних версиях ошибка, из-за которой она записывала имя устройства, даже если распределение не удалось? В исходном коде я вижу, что интерфейс для выделения в ядре существует только начиная с ядра 3.1. Может быть, это ошибка со старым интерфейсом, которая требует, чтобы losetupутилита выполняла поиск?
Жиль "ТАК - перестань быть злым"

О, это действительно исправлено в более поздних версиях. util-linux-2.26.2, кажется, работает, util-linux-2.24.1 многократно печатает /dev/loop14и тому подобное, и устройство может в итоге вообще отсутствовать. Возможно исправление любезно предоставлено /dev/loop-control, раньше оно просто смотрело на /proc/partitions...
frostschutz

@frostschutz Поскольку util-linux 2.21 losetupиспользует, /dev/loop-controlесли присутствует, и это не похоже на то, что это может иметь условие гонки: распределение происходит в ядре, и печать пути устройства - это последнее, что делает утилита.
Жиль "ТАК - перестань быть злым"

6

Я понял. Хотя я не уверен, какова проблема с разрешением, я могу вместо этого сначала выстрелить, а потом спросить так:

sudo losetup -f myfile.img
ld=$(losetup -j myfile.img | grep -o "/dev/loop[0-9]*")
dostuffwith $ld

2

Вы можете использовать flock:

  tryagain=1
  while [[ $tryagain -ne 0 ]]; do
    ld=`losetup -f`
    flock -n $ld -c "losetup $ld myfile.img"
    tryagain=$?
  done

Идея в том, что вы пытаетесь и flockфайл устройства цикла; если другой экземпляр того же скрипта получит его первым, он получит вызов losetup $ld myfile.imgи flockвернет 0. Для скрипта, который проиграл гонку, losetupне будет вызван и flockвернется 1, что приведет к повторению цикла.

Для более см man flock.


0

Если все, что вы хотите сделать с образом в качестве петлевого устройства, это смонтировать его как файловую систему и работать с содержимым, mountкоманда может позаботиться об этом автоматически.

mount -o loop myfile.img /tmp/mountpoint

На самом деле, в моем случае, я особенно хочу, чтобы он не был смонтирован - я использую его как блочное устройство, а не файловую систему.
AJMansfield

@AJMansfield Кроме монтирования, что вы можете сделать с блочным устройством, которое вы не можете сделать с файлом?
Random832

Вы не можете отформатировать его как пространство подкачки при установке полного диска btrfs. Я намеревался использовать файл для пространства подкачки, так как btrfs не поддерживает операции, необходимые для файлов подкачки, и у меня не может быть реального раздела подкачки с полной установкой btrfs на диске.
AJMansfield
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.