cpとmvとinodeの話


実行中のファイルに対して、
cpで上書きする場合だと、Text file busyで置き換えられず、
mvだと何も聞かれず置き換えられます。

この挙動の違いについて、まとめと実験。

inodeを見ると、それとなく分かります。

https://ja.wikipedia.org/wiki/Inode

以下引用

リンクを全く持たない inode もありうる。
通常そのようなファイルはディスクから削除され、
そのリソース（ディスクブロック）はファイル削除処理の過程で
再利用のために解放されるが、何らかのプロセスが
そのファイルを使用中ならば、アクセスし続けることができ、
最後にクローズされるときに削除処理が行われる。

このため、プログラムを改版（リコンパイル）するときは、
以前の実行ファイルをまず削除して、新しい版の実行ファイルは
新たな inode で作成されるようにすることが推奨される。
これにより、古い版が実行中であっても何ら問題なく処理を続行することになる。

（訳注：削除しないで上書きすると、実行中の実行ファイルが書き換えられるため、
メモリ管理の実装によってはおかしな状態が発生する）。

引用終わり

つまり、
以下のファイルがあるとして。

・/sa/sbin/org_exec inode=100 inodeの中身=abc
・/sa/sbin/new_exec inode=200 inodeの中身=123

cp /sa/sbin/new_exec /sa/sbin/org_exec

とした場合、inode=100の内容を、
inode=200の内容で上書きするので、
以下のようになる。

・/sa/sbin/org_exec inode=100 inodeの中身=123
・/sa/sbin/new_exec inode=200 inodeの中身=123

org_execを使っている人は、突然abcが123になって、
なんぞ？っとなる。そしてcpコマンドは、こうなるとまずいので、
Text file busyを出して、失敗にする。

mv /sa/sbin/new_exec /sa/sbin/org_exec

とした場合、/sa/sbin/org_exec inode=200となり、
inode=100は、何もファイルパスを指さない状態となるだけになる。

・ファイルパスは無い　inode=100 inodeの中身=abc
・/sa/sbin/org_exec inode=200 inodeの中身=123
・/sa/sbin/new_execは無くなった状態になる（org_execにmvしているので）

inode=100はファイルパスはなくなるが、inodeの中身は変わっていないので、
現状誰かが使用していたとしても、問題なくabc、という中身を見ることができる。
なので、実行中のファイルに、別のファイルをmvしても問題ないので、
mvは成功する。

そして、inode=100の内容は、それを使っているプロセスが
ファイルをクローズすれば再利用可能な状態に戻る。


以下、実際にやってみた結果。
cpだとエラーがでて失敗し、
mvは成功している。

mvはinodeの内容自体は変えず、
inodeとファイルパスの関連を変えているだけ、
というのが分かるはず。

そしてmvした後も、a.exeは動き続けている。
ただしこれは、mvする前の状態のa.exeである点に
注意が必要。

[root@localhost ~]#
[root@localhost ~]# stat a.exe
  File: `a.exe'
  Size: 6777            Blocks: 16         IO Block: 4096   regular file
Device: 802h/2050d      Inode: 790530      Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2015-09-09 13:14:19.642295609 +0900
Modify: 2015-09-09 13:14:18.178283786 +0900
Change: 2015-09-09 13:14:18.178283786 +0900
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# stat b.exe
  File: `b.exe'
  Size: 6800            Blocks: 16         IO Block: 4096   regular file
Device: 802h/2050d      Inode: 790537      Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2015-09-09 13:16:20.022259845 +0900
Modify: 2015-09-09 13:16:17.200237245 +0900
Change: 2015-09-09 13:16:17.200237245 +0900
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# ./a.exe &
[1] 21205
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# cp b.exe a.exe
cp: overwrite `a.exe'? y
cp: cannot create regular file `a.exe': Text file busy
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# mv b.exe a.exe
mv: overwrite `a.exe'? y
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# stat a.exe
  File: `a.exe'
  Size: 6800            Blocks: 16         IO Block: 4096   regular file
Device: 802h/2050d      Inode: 790537      Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2015-09-09 13:16:20.022259845 +0900
Modify: 2015-09-09 13:16:17.200237245 +0900
Change: 2015-09-09 13:17:12.565688924 +0900
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# ps aux | grep a.exe
root     21205  0.0  0.0  11736   828 pts/0    S    13:16   0:00 ./a.exe
root     31922  0.0  0.0 103304   884 pts/0    S+   13:54   0:00 grep a.exe

 
間違い等あれば、指摘ください。。