ただの技術メモ

個人備忘録

ディレクトリやファイルの仕組み

ディレクトリやファイルの仕組みを追っていて、ファイルシステムの仕組みやストレージの構成などについて調べたのでまとめておく。 コマンドなどはUbuntu 20.04.2 LTSでの実行結果です。

ストレージの構成

ストレージはパーティションという複数の領域に仮想的に分割して使用される。

パーティション分割することで故障時に影響を小さくするなど様々なメリットがある。 パーティション分割していないと、例えばOSに不具合があって再インストールしなければいけないだけなのに、OS以外のデータも削除されてしまうなど辛い状況が発生する。

パーティションはブロックという単位で利用される。例えば、EXT2というファイルシステムではパーティションを複数のブロックグループに分割する。 そのブロックグループの中はいくつかのブロックで構成されており、中でも「inodeテーブル」と「データブロック」の2つが多くを占める。

f:id:chann_r:20220209230620g:plain
EXT2の構造
linuxjf.osdn.jp

inodeテーブルはinodeを保存する領域で、データブロックは実際のデータを保存する領域である。

inodeについて

inodeはファイルやディレクトリのメタデータのことで様々なデータが含まれている(ファイルシステムによって変わる) 例えば、ファイルサイズや時刻情報、ファイル内のデータが格納されているデータブロックへのポインタなどが含まれている。

inodeの一部はstatコマンドなどでも見ることができる。

user-name:~/test-dir$ stat test.txt
  File: test.txt
  Size: 9           Blocks: 8          IO Block: 4096   regular file
Device: fc02h/64514d    Inode: 1325826     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/  hysrtr)   Gid: ( 1000/  hysrtr)
Access: 2022-02-08 01:45:21.404777480 +0900
Modify: 2022-02-08 01:45:21.404777480 +0900
Change: 2022-02-08 01:45:21.408777602 +0900
 Birth: -

inodeには番号が振られておりlsコマンドに-iオプションを付けることで確認できる。

user-name:~/test-dir$ ls -i
1325826 test.txt

ちなみにファイルの中身はこんな感じ

user-name:~/test-dir$ cat test.txt
hogehoge

inode内に保持しているデータブロックへのポインタによって、実際のデータが格納されているデータを取得できる。(残念ながらデータブロックへのポインタはstatコマンドから確認することができない)

ディレクトリやファイルについて

つまり、自分達が普段Finderなどで見ているいい感じのGUIは、実はinodeテーブルとデータブロックに格納されているデータを解釈したいい感じのものになっている。

ファイルやディレクトリには全てinode番号が振られておりinodeテーブルで管理されている。 ファイルの中身はデータブロックに格納されており、そのデータブロックへのポインタはinodeが持っている。

しかし、inodeには様々なメタデータが含まれているが、ファイル名は含まれていない。 ファイル名の情報を持っているのは(ややこしいけど)ファイルではなく、ディレクトリが持っている。 つまり、ディレクトリの実データはそのディレクトリに含まれるファイルのファイル名とそのinode番号である。

まとめると、データブロックにはファイルの場合はファイルの中身が格納され、ディレクトリの場合はディレクトリ配下のファイル名とそのinode番号が格納されている。

ファイルの読み込みについて

例えば、/Users/user-name/Desktop/test.txtというファイルの読み込みを考えてみる。実際にはキャッシュなどで高速化されている。

  1. まずルートディレクトリを参照する。ルートディレクトリのinode番号は多くの場合は2で固定されている。 inode番号を指定して、ルートディレクトリのinodeを参照する。 inode内にあるデータブロックへのポインタでデータブロックにアクセスし、ルートディレクトリ配下にあるディレクトリ名とそのinode番号を取得する。

  2. 次に/Usersディレクトリを参照する。手順1で/Usersディレクトリのinode番号は取得しているので、そのinode番号を指定してinodeを取得する。 inode内にあるデータブロックへのポインタでデータブロックにアクセスし、/Usersディレクトリ配下にあるディレクトリ名とそのinode番号を取得する。

  3. 2と同様の手順を繰り返す。

  4. 最後に/Users/user-name/Desktop/test.txtを参照する。1つ前の手順で/Users/user-name/Desktop/test.txtのinode番号は取得しているので、そのinode番号を指定してinodeを取得する。 inode内にあるデータブロックへのポインタでデータブロックにアクセスし、ファイル内容を取得する。

ファイルシステム関連のコマンドを触って理解する

まずファイルシステムを確認する。

user:~$ df -k|head -6
Filesystem     1K-blocks     Used Available Use% Mounted on
udev              200376        0    200376   0% /dev
tmpfs              48956     2204     46752   5% /run
/dev/vda2       30828432 28297208   1047216  97% /
tmpfs             244768        0    244768   0% /dev/shm
tmpfs               5120        0      5120   0% /run/lock

/dev/vdaというファイルシステムに注目して、情報を出力する。

user-name:~$ df -i /dev/vda2
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/vda2      1966080 252870 1713210   13% /

Inodesにはこのファイルシステムのinodeの総数が出力される。

debugfsのコマンドでinode番号を指定して少し詳細にinodeの内容を出力できる。

user-name:~$ sudo debugfs -R 'stat <1325826>' /dev/vda2 | cat
debugfs 1.45.5 (07-Jan-2020)
Inode: 1325826   Type: regular    Mode:  0644   Flags: 0x80000
Generation: 2680939443    Version: 0x00000000:00000001
User:  1000   Group:  1000   Project:     0   Size: 9
File ACL: 0
Links: 1   Blockcount: 8
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x62014ca1:6175ce08 -- Tue Feb  8 01:45:21 2022
 atime: 0x62014ca1:6081a820 -- Tue Feb  8 01:45:21 2022
 mtime: 0x62014ca1:6081a820 -- Tue Feb  8 01:45:21 2022
crtime: 0x62014ca1:6081a820 -- Tue Feb  8 01:45:21 2022
Size of extra inode fields: 32
Inode checksum: 0x1dc7c3f7
EXTENTS:
(0):5452907

imapコマンドを使うことで指定したinodeがどのブロックグループのどのブロックにあるかを出力できる。

user-name:~$ sudo debugfs -R 'imap <1325826>' /dev/vda2
debugfs 1.45.5 (07-Jan-2020)
Inode 1325826 is part of block group 161
    located at block 5243856, offset 0x0100

catコマンドで使うことで指定したinodeが指すファイルの内容を出力できる。

user-name:~$ sudo debugfs -R 'cat <1325826>' /dev/vda2 | cat
debugfs 1.45.5 (07-Jan-2020)
hogehoge

対象がディレクトリだと以下のようにファイル名やら何やらを格納しているんだなと確認できる。

user-name:~$ sudo debugfs -R 'cat <1327105>' /dev/vda2 | cat
debugfs 1.45.5 (07-Jan-2020)
@
 .�
.test.txt.swp{��test.txt~.swx
                             �N��

man7.org