ディレクトリやファイルの仕組み
ディレクトリやファイルの仕組みを追っていて、ファイルシステムの仕組みやストレージの構成などについて調べたのでまとめておく。
コマンドなどはUbuntu 20.04.2 LTS
での実行結果です。
ストレージの構成
ストレージはパーティションという複数の領域に仮想的に分割して使用される。
パーティション分割することで故障時に影響を小さくするなど様々なメリットがある。 パーティション分割していないと、例えばOSに不具合があって再インストールしなければいけないだけなのに、OS以外のデータも削除されてしまうなど辛い状況が発生する。
パーティションはブロックという単位で利用される。例えば、EXT2というファイルシステムではパーティションを複数のブロックグループに分割する。 そのブロックグループの中はいくつかのブロックで構成されており、中でも「inodeテーブル」と「データブロック」の2つが多くを占める。
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
というファイルの読み込みを考えてみる。実際にはキャッシュなどで高速化されている。
まずルートディレクトリを参照する。ルートディレクトリのinode番号は多くの場合は2で固定されている。 inode番号を指定して、ルートディレクトリのinodeを参照する。 inode内にあるデータブロックへのポインタでデータブロックにアクセスし、ルートディレクトリ配下にあるディレクトリ名とそのinode番号を取得する。
次に/Usersディレクトリを参照する。手順1で/Usersディレクトリのinode番号は取得しているので、そのinode番号を指定してinodeを取得する。 inode内にあるデータブロックへのポインタでデータブロックにアクセスし、/Usersディレクトリ配下にあるディレクトリ名とそのinode番号を取得する。
2と同様の手順を繰り返す。
最後に
/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��