Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 以原子方式移动目录_Linux_Bash_Atomic - Fatal编程技术网

Linux 以原子方式移动目录

Linux 以原子方式移动目录,linux,bash,atomic,Linux,Bash,Atomic,我在同一个父目录中有两个目录。调用父目录base和子目录alpha和bravo。我想用bravo替换alpha。最简单的方法是: rm -rf alpha mv bravo alpha mv命令是原子的,但rm-rf不是。在bash中有没有一种简单的方法可以用bravo原子化地替换alpha?如果不是,有没有复杂的方法 增编: 顺便说一句,如果目录在短时间内不存在,这不是一个无法克服的问题。只有一个地方尝试访问alpha,它会在执行任何关键操作之前检查alpha是否存在。如果没有,它将给出一条

我在同一个父目录中有两个目录。调用父目录base和子目录alphabravo。我想用bravo替换alpha。最简单的方法是:

rm -rf alpha
mv bravo alpha
mv命令是原子的,但rm-rf不是。在bash中有没有一种简单的方法可以用bravo原子化地替换alpha?如果不是,有没有复杂的方法

增编:


顺便说一句,如果目录在短时间内不存在,这不是一个无法克服的问题。只有一个地方尝试访问alpha,它会在执行任何关键操作之前检查alpha是否存在。如果没有,它将给出一条错误消息。但如果有办法做到这一点,那就太好了。:)也许有某种方法可以直接修改inode,或者其他什么…

我不相信有任何原子方法可以做到这一点。您的最佳选择是执行以下操作:

mv alpha delme
mv bravo alpha
rm -rf delme
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");

如果你是说两个操作都是原子的,我不这么认为。最接近的是:

mv alpha delta
mv bravo alpha
rm -rf delta
但在阿尔法不存在的地方,仍然会有一个小窗口

为了最大限度地降低在alpha不存在时尝试使用alpha的可能性,您可以(如果您有权限):

这将在执行
mv
操作时大大提高流程优先级


如果,正如您在附录中所说,只有一个地方可以检查alpha和it错误,如果它不在那里,您可以立即将该代码更改为not error,但在短时间内重试(对于两次
mv
操作,很容易在亚秒的时间内重试)-除非您非常频繁地更换alpha,否则这些重试应该可以缓解任何问题。

即使您直接访问inode,也无法在用户空间中自动交换inode值。

如果使用符号链接,您可以这样做:

假设alpha是指向目录alpha_1的符号链接,您希望将符号链接切换到指向alpha_2。以下是切换前的情况:

$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
要使alpha引用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

使用一个单独的、有保证的原子操作作为信号量

因此,如果创建和删除文件操作是原子操作:

1) 创建一个名为“信号量”的文件

2) 如果且仅当该操作成功(与现有文件无冲突),则执行该操作(处理alpha或移动目录,具体取决于进程)


3) rm信号。

需要记住的是,如果在发生此移动/删除时,进程打开了alpha中的任何文件,则进程将不会注意到,并且当文件关闭并最终删除时,写入的任何数据都将丢失。

担心操作的原子性质是毫无意义的。问题是,另一个任务对alpha的访问无论如何都不会是原子的

Oddthinking的信号量方法是唯一的出路


如果无法修改其他任务,则必须确保它未运行,然后再进行替换。

为什么不执行以下操作:

rm -rf alpha/*
mv bravo/* alpha/
rm -rf bravo/

这意味着alpha中的所有内容都会被销毁,alpha永远不会被删除,所有内容都会被移动。

文档部分对其升级锁定协议进行了详细描述,以控制崩溃后的并发读取、独占写入和回滚。有些想法在这里适用。

mv和ln可用于原子操作。我使用ln(1)以原子方式部署web应用程序

替换符号链接的正确方法是使用ln-nsf

ln -nsf <target> <link_name>

最终的解决方案是结合符号链接和重命名方法:

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

当然,访问alpha的应用程序必须能够处理路径中的符号链接变化。

这里学习David的解决方案,它是完全原子化的。。。您遇到的唯一问题是
mv
-T
选项不是POSIX,因此某些POSIX操作系统可能不支持它(FreeBSD、Solaris等)。只需稍加修改,即可将此方法更改为完全原子化,并可移植到所有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 ./

例如通过:

这应该可以做到:

mkdir bravo_dir alpha_dir
ln -s bravo_dir bravo
ln -s alpha_dir alpha
mv -fT bravo alpha
strace mv-布拉沃阿尔法英尺显示:

rename("bravo", "alpha")

对我来说,这看起来非常原子。

也可以使用
unionfs fuse
在某个前缀(
Z
)中一次性替换整个内容部分:

#mkdir a b c Z
#触摸a/1 b/2 c/3
#ln-sax
#ln-sby
#unionfs X=RW:Y=RW Z
#shopt-s环球之星
#文件**
a:目录
a/1:空
b:目录
b/2:空的
c:目录
c/3:空
X:指向
Y:指向b的符号链接
Z:目录
Z/1:空
Z/2:空
#ln-sfncy
#文件**/*
a:目录
a/1:空
b:目录
b/2:空的
c:目录
c/3:空
X:指向
X/1:空
Y:指向c的符号链接
Y/3:空
Z:目录
Z/1:空
Z/3:空
#fusermount-uz
#rm-r a b c X Y Z

自Linux 3.15以来,新的
重命名2
系统调用可以在同一文件系统上自动交换两个路径。然而,它甚至还没有glibc包装,更不用说coreutils访问它的方式了。所以它看起来像这样:

mv alpha delme
mv bravo alpha
rm -rf delme
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
wrapper,请参阅。)

这就是说,其他人提到的符号链接解决方案既简单又可移植,因此除非
bravo
已经存在,并且您必须对其进行原子更新,否则请使用符号链接


2020更新:此系统调用的glibc包装器
int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
renameat2(dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");