[Linux][C言語]ファイルディスクリプタとハードリンクの関係について


C言語でファイルの読み書きをするときにはファイルを指し示すファイルディスクリプタという整数値を使います。プロセスがファイルを開いているときに、ハードリンクの変更、削除を試してみたのですが、プロセスは影響を受けませんでした。この事から、ファイルディスクリプタはハードリンクに紐づくものではなく、ファイルの実体と紐付いていると考えられます。イメージはこんな感じになります。

検証内容は以下です。

検証環境

コンパイラ:gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
OS:Ubuntu 20.04.2 LTS (Focal Fossa)

ハードリンクの変更

ファイルディスクリプタが実体と紐づくなら、ハードリンクを変更してもプログラムは影響を受けないはずです。作成したプログラムを次に示します。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define FILE_PATH "A.txt"

int
main(void)
{
  int f = open(FILE_PATH, O_WRONLY);
  if (f < 0) {
    perror("");
    exit(EXIT_FAILURE);
  }
  fprintf(stderr, "open file %s\n", FILE_PATH);

  sleep(10);

  char *buf = "HELLO!";
  if (write(f, buf, sizeof buf) < 0) {
    perror("");
    exit(EXIT_FAILURE);
  }
  fprintf(stderr, "write file %s\n", FILE_PATH);

  if (close(f) < 0) {
    perror("");
    exit(EXIT_FAILURE);
  }
  fprintf(stderr, "close file %s\n", FILE_PATH);

  exit(EXIT_SUCCESS);
}

このプログラムはファイルを開いたあと、10秒待機し、HELLO! と書き込んでからファイルを閉じます。その間に mv コマンドでファイルのハードリンクを変更してみます。分かりやすくするために、シェルの操作は行頭に $ をつけました。

$ ./open_close &
[1] 30887
open file A.txt
$ mv A.txt B.txt
write file A.txt
close file A.txt

mv コマンドでハードリンクを B.txt 変更しましたが、変更後のファイルに正しく書き込まれています。

$ cat B.txt
HELLO!

この結果から、単にハードリンクが変更されただけで、プログラムが使っているファイルディスクリプタは影響を受けていないことがわかります。

ハードリンクの削除

続いて、ハードリングを削除してみます。ファイルディスクリプタが実体と紐づくなら、ハードリンクを削除しても影響を受けないはずです。

先程と同じプログラムを使い、ファイルが開かれている間に rm コマンドでハードリンクを削除してみます。

$ ./open_close &
[1] 30949
open file A.txt
$ rm A.txt
write file A.txt
close file A.txt

ファイルは削除してしまったので、プログラム終了後も存在しません。

$ ls A.txt
ls: 'A.txt' にアクセスできません: そのようなファイルやディレクトリはありません

この結果からも、ハードリンクが削除されただけなので、実体を指し示していたファイルディスクリプタは影響を受けていません。

参考文献

書籍: ふつうのLinuxプログラミング第2版 青木 峰郎著