Перемещение каталога атомарно - PullRequest
32 голосов
/ 21 ноября 2008

У меня есть два каталога в одном родительском каталоге. Вызовите родительский каталог base и дочерние каталоги alpha и bravo . Я хочу заменить alpha на bravo . Самый простой способ:

rm -rf alpha
mv bravo alpha

Команда mv является атомарной, но команда rm -rf - нет. Есть ли в bash простой способ атомарной замены alpha на bravo ? Если нет, то есть ли сложный путь?

ДОПОЛНЕНИЕ:

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

Ответы [ 15 ]

43 голосов
/ 04 июня 2012

Окончательное решение сочетает символическую ссылку и переименование:

mkdir alpha_real
ln -s alpha_real alpha

# now use "alpha"

mkdir beta_real
ln -s beta_real tmp 

# atomically rename "tmp" to "alpha"
# use -T to actually replace "alpha" instead of moving *into* "alpha"
mv -T tmp alpha

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

14 голосов
/ 21 ноября 2008

Вы можете сделать это, если используете символические ссылки:

Допустим, альфа является символической ссылкой на каталог alpha_1, и вы хотите переключить символическую ссылку, чтобы она указала на alpha_2. Вот как это выглядит до переключения:

$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2

Чтобы сделать альфа ссылкой на alpha_2, используйте ln -nsf:

$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2

Теперь вы можете удалить старый каталог:

$ rm -rf alpha_1

Обратите внимание, что это на самом деле не полностью атомарная операция, но она происходит очень быстро, так как команда "ln" отменяет связь, а затем немедленно воссоздает символическую ссылку. Вы можете проверить это поведение с помощью strace:

$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha")             = -1 EEXIST (File exists)
unlink("alpha")                         = 0
symlink("alpha_2", "alpha")             = 0
...

Вы можете повторить эту процедуру по желанию: например, когда у вас новая версия, alpha_3:

$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2
12 голосов
/ 05 июля 2013

Принятие решения Дэвида здесь, которое является полностью атомарным ... единственная проблема, с которой вы столкнетесь, состоит в том, что опция -T для mv не является POSIX, и поэтому некоторые операционные системы POSIX могут не поддерживать его (FreeBSD, Solaris и т. Д. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). При небольшом изменении этот подход может быть изменен на полностью атомарный и переносимый на все операционные системы POSIX:

mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./

через: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/

6 голосов
/ 21 ноября 2008

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

Итак, если операции создания и удаления файла являются атомарными:

1) создайте файл с именем "семафор".

2) Если и только если это успешно (без конфликта с существующим файлом), выполните операцию (либо обработайте альфа, либо переместите каталог, в зависимости от процесса)

3) rm семафор.

6 голосов
/ 21 ноября 2008

Если вы имеете в виду атомарный в обеих операциях, я не верю. Ближайшим будет:

mv alpha delta
mv bravo alpha
rm -rf delta

но в нем все еще будет маленькое окно, в котором альфа не существует.

Чтобы свести к минимуму вероятность того, что кто-либо попытается использовать альфу, пока ее там нет, вы можете (если у вас есть полномочия):

nice --20 ( mv alpha delta ; mv bravo alpha )
rm -rf delta

, что существенно повысит приоритет вашего процесса во время выполнения операций mv.

Если, как вы говорите в своем приложении, есть только одно место, которое проверяет альфа, и если оно отсутствует, вы можете изменить этот код так, чтобы он не выдавал ошибку сразу, а повторить попытку за короткое время (легко за секунду для две операции mv) - эти повторные попытки должны устранить любую проблему, если вы не заменяете alpha очень часто.

4 голосов
/ 08 ноября 2009

Раздел документации SQLite Блокировка файлов и параллелизм в SQLite версии 3 содержит хорошо написанное описание расширяющегося протокола блокировки для управления одновременным чтением, эксклюзивный запись и откат после сбоя. Некоторые из этих идей применимы здесь.

3 голосов
/ 15 мая 2018

Начиная с Linux 3.15, новый системный вызов renameat2 может атомарно обмениваться двумя путями в одной файловой системе. Тем не менее, даже нет glibc оболочки для него, не говоря уже о coreutils способ доступа к нему. Так это будет выглядеть примерно так:

int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
syscall(SYS_renameat2, dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");

(Конечно, вы должны сделать правильную обработку ошибок и т. Д. - см. эту суть для более сложной renameat2 оболочки.)

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

3 голосов
/ 07 января 2014

Это должно сработать:

mkdir bravo_dir alpha_dir
ln -s bravo_dir bravo
ln -s alpha_dir alpha
mv -fT bravo alpha

strace mv -fT bravo alpha показывает:

rename("bravo", "alpha")

, что выглядит довольно атомно для меня.

1 голос
/ 21 ноября 2008

Беспокойство об атомной природе операции бессмысленно. Дело в том, что доступ к альфе другой задачей все равно не будет атомарным.

Семафорный подход к странному мышлению - единственный путь.

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

1 голос
/ 21 ноября 2008

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

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