dir*_*ide 35 linux bash atomic
我在同一个父目录中有两个目录.调用父目录库和子目录alpha和bravo.我想用bravo替换alpha.最简单的方法是:
rm -rf alpha
mv bravo alpha
Run Code Online (Sandbox Code Playgroud)
mv命令是原子的,但rm -rf不是.在bash中有一种简单的方法可以用bravo原子替换alpha吗?如果没有,有复杂的方法吗?
附录:
如果目录在短时间内不存在,那么这不是一个不可逾越的问题.只有一个地方试图访问alpha,它会在做任何关键事情之前检查alpha是否存在.如果没有,它会给出错误消息.但如果有办法做到这一点会很好.:)也许有一些方法可以直接修改inode,或者某些东西......
Dav*_*itt 46
最终的解决方案是结合符号链接和重命名方法:
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
Run Code Online (Sandbox Code Playgroud)
当然,访问alpha的应用程序必须能够处理路径中更改的符号链接.
Dou*_*rie 17
如果使用符号链接,则可以执行此操作:
假设alpha是目录alpha_1的符号链接,并且您希望将符号链接切换为指向alpha_2.这是切换前的样子:
$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Run Code Online (Sandbox Code Playgroud)
要使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
Run Code Online (Sandbox Code Playgroud)
现在您可以删除旧目录:
$ rm -rf alpha_1
Run Code Online (Sandbox Code Playgroud)
请注意,这实际上不是完全原子操作,但它确实很快发生,因为"ln"命令都取消链接,然后立即重新创建符号链接.您可以使用strace验证此行为:
$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...
Run Code Online (Sandbox Code Playgroud)
您可以根据需要重复此过程:例如,当您有新版本时,alpha_3:
$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2
Run Code Online (Sandbox Code Playgroud)
小智 12
在这里接受David的解决方案,这是完全原子的...你遇到的唯一问题是非POSIX 的-T选项mv,因此某些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 ./
Run Code Online (Sandbox Code Playgroud)
exaple via:http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
Luc*_*ter 11
从 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");
Run Code Online (Sandbox Code Playgroud)
(当然,您应该进行适当的错误处理等 -有关更复杂的包装器,请参阅此要点renameat2。)
也就是说 - 其他人提到的符号链接解决方案既简单又便携,因此除非bravo已经存在并且您必须自动更新它,否则请使用符号链接。
2020 年更新:自 2018 年 8 月 1 日发布的 glibc 2.28(Debian Stretch,Fedora 29)以来,此系统调用的 glibc 包装器可用。尽管如此,它仍然无法通过 coreutils 访问。
int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
renameat2(dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");
Run Code Online (Sandbox Code Playgroud)
如果你的意思是两种操作都是原子的,我不相信.最接近的是:
mv alpha delta
mv bravo alpha
rm -rf delta
Run Code Online (Sandbox Code Playgroud)
但那仍然会有一个不存在alpha的小窗口.
为了最大限度地减少任何尝试使用alpha的可能性,而不是那里你可以(如果你有权限):
nice --20 ( mv alpha delta ; mv bravo alpha )
rm -rf delta
Run Code Online (Sandbox Code Playgroud)
这将在mv运营发生时大大提高您的流程优先级.
如果你正如你在附录中所说的那样,只有一个地方检查alpha并且如果它不存在就会出错,你可以立即将该代码更改为不错误,但是在短时间内再试一次(两次mv操作很容易再次测试) -除非您要更换阿尔法这些重试应减轻任何问题非常频繁.
使用单独的,保证原子的操作来充当信号量.
因此,如果创建和删除文件操作是原子操作:
1)创建一个名为"信号量"的文件.
2)当且仅当它成功(与现有文件没有冲突)时,执行操作(处理alpha或移动目录,具体取决于进程)
3)rm信号量.
小智 5
这应该可以解决问题:
mkdir bravo_dir alpha_dir
ln -s bravo_dir bravo
ln -s alpha_dir alpha
mv -fT bravo alpha
Run Code Online (Sandbox Code Playgroud)
strace mv -fT bravo alpha 显示:
rename("bravo", "alpha")
Run Code Online (Sandbox Code Playgroud)
对我来说这看起来非常原子。