LoginSignup
11
10

More than 5 years have passed since last update.

アトミックなシンボリックリンク更新

Posted at

はじめに

Linuxでシンボリックリンクの参照先を更新したい場合、以下のようにするのが簡単です。

> ln -snf src dst

今回他のプロセスがdstにアクセス中に更新したかったので、この方法で安全に更新できるかどうか調べてみました。
確認した環境はRHEL7.2/Gentoo Linuxですが大抵のLinux環境で同じような挙動になるのではないかと思います。
(POSIXとかで決まってそうな気もしますが調べてません…)

straceで調べる

まずlnコマンドでの更新の様子を見てみます。

> strace ln -snf src dst
...
symlink("src", "dst")                   = -1 EEXIST (File exists)
unlink("dst")                           = 0
symlink("src", "dst")                   = 0
...

このようにlnは1コマンドですが、システムコール的には「unlinkで削除してからsymlinkでシンボリックリンクを作る」という実装になっているようです。
そのためunlinksymlinkの間にdstにアクセスされると失敗する可能性があります。

更新中に割り込めるか?

2つのシステムコールの間にアクセスされる可能性はありそうだったので、
実際に起きるかどうか以下のようなシェルスクリプトで実験してみました。

ln.sh
#!/bin/sh
echo test1 > src1
echo test2 > src2
while :
do
    ln -snf src1 dst
    ln -snf src2 dst
done
cat.sh
#!/bin/sh
while :
do
    cat dst
done

ln.shdstsrc1/src2に交互に更新し、cat.shdstを読みます。この2つを同時に実行すると以下のような結果になりました。

> ./cat.sh
test1
test2
test1
test2
test1
cat: dst: そのようなファイルやディレクトリはありません
test2
test1
test2
test1
test2

上記は抜粋ですが20~30回に1回程度はcatに失敗するようです。

アトミックなシンボリックリンク更新

先ほどのln.shの代わりに

ln_atomic.sh
#!/bin/sh
echo test1 > src1
echo test2 > src2
while :
do
    ln -s src1 tmp
    mv -Tf tmp dst
    ln -s src2 tmp
    mv -Tf tmp dst
done

このln_atomic.shを使ってみると

> ./cat.sh
test1
test2
test1
test2
test1
test2
test1
test2
test1
test2

という感じでcatの失敗は発生しなくなりました。

実際にシンボリックリンクを更新しているのはmvコマンドです。このコマンドは同一ファイルシステム内に限り、単一のシステムコールrenameで実装されています。従って更新中に割り込まれることはありません。

ちなみにファイルシステム間のmvは単にファイルコピー+削除になるようです。
そのためsrcdstはファイルシステムが異なっていてもよいのですが、tmpdstは同一ファイルシステムになければいけません。

まとめ

シンボリックリンクをアトミックに更新したい場合は以下のようにするといいようです。
tmpdstが同一ファイルシステムになるよう注意)

> ln -s src tmp
> mv -Tf tmp dst
11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10