動作環境は Docker コンテナ上の Ubuntu:22.04 です。
docker pull ubuntu:22.04
docker run -it --privileged ubuntu:22.04
#Linux ファイルシステムとは
Linux においてファイルシステムは、ストレージデバイス上のデータを永続的に保存・管理するための仕組みであり、カーネルとユーザープログラムの間を橋渡しする重要な役割を担っている。
ファイルシステムの主な役割は以下の通り。
- データの永続化: 電源を切ってもデータが失われないようにストレージへ書き込む
- ディレクトリツリー構造: すべてのファイルを
/(ルート) を頂点とした木構造で管理する - メタデータ管理: ファイルの所有者・パーミッション・タイムスタンプを保持する
- アクセス制御: カーネルがパーミッションチェックを行い、不正アクセスを防ぐ
カーネルとのレイヤー関係は以下のようになっている。
flowchart TD
A["ユーザープログラム (open / read / write)"]
B["システムコール層"]
C["VFS (Virtual File System)\n統一インターフェース"]
D["具体的な FS ドライバ (ext4 / xfs / tmpfs ...)"]
E["ブロックデバイス層 / メモリ"]
A --> B --> C --> D --> E
#VFS (Virtual File System)
##なぜ VFS が必要か
Linux はディスクに書き込む ext4 や xfs、メモリ上に展開する tmpfs、ネットワーク越しにアクセスする NFS など、用途や実装の異なる多種多様なファイルシステムをサポートしている。
VFS がなければ、open() や read() を呼び出すたびにプログラムが「どの FS か」を意識する必要がある。
VFS はこの差異を吸収し、どのファイルシステムに対しても同一のシステムコールインターフェースを提供する抽象化レイヤーである。
##主要な抽象オブジェクト
VFS は 4 つのオブジェクトでファイルシステムの状態を表現する。
| オブジェクト | 役割 |
|---|---|
superblock | マウントされたファイルシステム全体の情報 (ブロックサイズ・総 inode 数・空き容量など) |
inode | ファイルやディレクトリのメタデータ (パーミッション・所有者・サイズ・データブロックの場所) |
dentry | ディレクトリエントリ。パス名の各コンポーネント (ディレクトリや ファイル名) と inode の対応を保持するキャッシュ |
file | プロセスがオープンしているファイルの状態 (現在のファイルオフセットなど) |
##システムコールから FS ドライバへの流れ
open("/etc/hostname", O_RDONLY)
↓ システムコール
VFS: パスを dentry キャッシュで解決し、対応する inode を取得
↓
inode の i_op (inode operations) を呼び出す
↓
ext4 / xfs などの具体的な FS ドライバが実処理を担う
↓
ファイルディスクリプタを返す
各 FS ドライバは file_operations や inode_operations といった関数ポインタのテーブルを VFS に登録しており、VFS はそれを経由して具体的な処理を委譲する。
#inode
##inode とは
inode (index node) はファイルやディレクトリのメタデータを格納するデータ構造で、ファイルシステムごとにディスク上に固定数が確保される。
inode が持つ主な情報は以下の通り。
| フィールド | 内容 |
|---|---|
| ファイルタイプ | 通常ファイル・ディレクトリ・シンボリックリンク・デバイスファイルなど |
| パーミッション | 所有者・グループ・その他への rwx 権限 |
| 所有者 | UID / GID |
| タイムスタンプ | atime (最終アクセス) / mtime (最終更新) / ctime (メタデータ変更) |
| ファイルサイズ | バイト単位 |
| リンクカウント | この inode を指しているハードリンクの数 |
| データブロックポインタ | ファイルの実データが格納されたブロックの場所 |
ファイル名は inode に含まれない。 ファイル名はディレクトリエントリ (dentry) が「名前 → inode 番号」のマッピングとして保持している。これがハードリンクの根拠になる。
##stat / ls -i / df -i コマンドで確認する
# inode の詳細情報を表示
stat /etc/hostname
File: /etc/hostname
Size: 13 Blocks: 8 IO Block: 4096 regular file
Device: fd11h/64785d Inode: 1577295 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2026-03-28 01:47:39.736176500 +0000
Modify: 2026-03-28 01:47:39.736176500 +0000
Change: 2026-03-28 01:47:39.736176500 +0000
Birth: 2026-03-28 01:47:39.736176500 +0000
# inode 番号を表示
# -i, --inode print the index number of each file
ls -i /etc/hostname
1577295 /etc/hostname
# ファイルシステム全体の inode 使用状況
# df --help
# Usage: df [OPTION]... [FILE]...
# Show information about the file system on which each FILE resides,
# or all file systems by default.
df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
overlay 6553600 216708 6336892 4% /
tmpfs 250802 213 250589 1% /dev
shm 250802 1 250801 1% /dev/shm
/dev/vdb1 6553600 216708 6336892 4% /etc/hosts
#ハードリンク・シンボリックリンク
##ハードリンク
ハードリンクは、既存の inode に対して別のディレクトリエントリ (名前) を追加することで作成される。同一の inode を指す複数の名前が存在する状態であり、inode のリンクカウントが増加する。
- inode 番号が同じため、どちらの名前で編集しても内容は同じ
- いずれかの名前を削除してもリンクカウントが 0 になるまでデータは消えない
- ディレクトリへのハードリンクは通常禁止 (ループが発生するため)
- 異なるファイルシステム間では作成できない
# ハードリンクを作成
ln original.txt hardlink.txt
# inode 番号が同じことを確認
ls -li original.txt hardlink.txt
556656 -rw-r--r-- 2 root root 12 Mar 28 01:55 hardlink.txt
556656 -rw-r--r-- 2 root root 12 Mar 28 01:55 original.txt
# ↑ 同じ inode 番号 (524300)、リンクカウントが 2
# 片方を削除してもデータは残る
rm original.txt
cat hardlink.txt # まだ読める
# リンクカウントが 1 に戻る
ls -li hardlink.txt
556656 -rw-r--r-- 1 root root 12 Mar 28 01:55 hardlink.txt
使用例として、pnpm はグローバルのストアから特定のプロジェクトへのハードリンクを作成することで、ディスク使用量を節約している。
##シンボリックリンク
シンボリックリンク (ソフトリンク) は、リンク先のパス文字列を内容として持つ独立したファイルである。inode 番号は元ファイルとは異なる。
- 別のファイルシステムや存在しないパスへもリンクできる
- ディレクトリへのリンクも作成可能
- リンク先が削除されると「壊れたリンク」になる (dangling symlink)
ls -lでリンク先が表示される
# シンボリックリンクを作成
ln -s /etc/hostname mylink
# inode 番号が異なることを確認
ls -li /etc/hostname mylink
1577295 -rw-r--r-- 1 root root 13 Mar 28 01:47 /etc/hostname
556656 lrwxrwxrwx 1 root root 13 Mar 29 09:05 mylink -> /etc/hostname
# ↑ inode 番号が異なる、パーミッションが l で始まる
# リンク先の確認
readlink mylink
/etc/hostname
##ハードリンク vs シンボリックリンクの比較
| 項目 | ハードリンク | シンボリックリンク |
|---|---|---|
| inode | 同じ | 別 |
| 別 FS またがり | 不可 | 可 |
| ディレクトリ | 不可 (通常) | 可 |
| リンク先削除後 | データ保持 | 壊れたリンクになる |
#ディレクトリ構造 (FHS)
##FHS とは
FHS (Filesystem Hierarchy Standard) は、Linux におけるディレクトリ構造の標準規格。各ディレクトリが何を格納すべきかを定義しており、主要なディストリビューション (Ubuntu・CentOS・Debian など) が準拠している。
これにより、ディストリビューションが異なっても設定ファイルやバイナリの場所が予測できるようになっている。
##主要ディレクトリ
| ディレクトリ | 内容 |
|---|---|
/ | ルートディレクトリ。すべてのファイルシステムの頂点 |
/bin | 基本的なコマンド (ls, cp, mv など)。シングルユーザーモードでも必要なもの |
/sbin | システム管理コマンド (root 向け: fdisk, ip など) |
/usr | ユーザー向けプログラムとデータ。/usr/bin, /usr/lib など |
/etc | システム全体の設定ファイル |
/home | 一般ユーザーのホームディレクトリ |
/root | root ユーザーのホームディレクトリ |
/var | 可変データ (ログ・スプール・キャッシュなど) |
/tmp | 一時ファイル。再起動で消えることがある |
/proc | プロセス情報などを公開する仮想ファイルシステム (procfs) |
/sys | デバイス・ドライバ情報を公開する仮想ファイルシステム (sysfs) |
/dev | デバイスファイル (ディスク・端末・乱数生成器など) |
/lib | 共有ライブラリ (/bin や /sbin が依存するもの) |
/mnt | 一時的なマウントポイント |
/media | リムーバブルメディア (USB・CD-ROM など) のマウントポイント |
/boot | ブートローダーとカーネルイメージ |
/opt | サードパーティのソフトウェア |
/run | 起動後に生成されるランタイムデータ (PID ファイルなど) |
現在はほとんどのケースで /bin は /usr/bin のシンボリックリンクとなっている。
昔は /bin と /usr/bin は別のディスクに置かれることがあり、 /bin にはシステム起動・緊急修復に最低限のコマンドが置かれていた。
(/usr がマウントされていなくても動く必要があったため。)
# ルートディレクトリの一覧
ls /
bin boot dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
# 主要ディレクトリのサイズ確認
# du --help
# Usage: du [OPTION]... [FILE]...
# or: du [OPTION]... --files0-from=F
# Summarize disk usage of the set of FILEs, recursively for directories.
du -sh /usr /var /etc
70M /usr
5.1M /var
640K /etc
#ファイルシステムの種類
##主要なファイルシステムの比較
| FS | 用途 | ジャーナリング | 最大ファイルサイズ | 特徴 |
|---|---|---|---|---|
| ext4 | Linux 汎用 | あり | 16TB | 安定性・実績が高い。Linux のデフォルト |
| xfs | 大容量・高性能 | あり | 8EB | 大規模ファイルに強い。RHEL のデフォルト |
| btrfs | スナップショット・RAID | あり | 16EB | CoW・スナップショット・圧縮が得意 |
| tmpfs | メモリ上の一時FS | なし | RAM 依存 | 再起動で消える。/tmp や /run に使用 |
| vfat | USB・SD カード互換 | なし | 4GB | Windows との互換性が高い |
##ジャーナリング
ジャーナリングとは、データを書き込む前に「これから行う操作」をジャーナル (ログ) に記録しておく仕組み。クラッシュや電源断が発生した場合でも、ジャーナルを再実行またはロールバックすることでファイルシステムの整合性を保てる。
ジャーナリングがない FS では、クラッシュ後に fsck で手動チェックが必要になる場合がある。
##コマンドで確認する
# ブロックデバイスの一覧とマウント情報
# lsblk --help
# List information about block devices.
# NAME device name
# MAJ:MIN major:minor device number
# RM removable device
# SIZE size of the device
# RO read-only device
# TYPE device type
# MOUNTPOINTS all locations where device is mounted
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 253:0 0 20G 0 disk
|-vda1 253:1 0 19G 0 part
|-vda15 253:15 0 99M 0 part
`-vda16 259:0 0 923M 0 part
vdb 253:16 0 100G 0 disk
`-vdb1 253:17 0 100G 0 part /etc/hosts
/etc/hostname
/etc/resolv.conf
vdc 253:32 0 53.1M 1 disk
# ファイルシステムの種類と使用状況
# -T, --print-type print file system type
df -T
Filesystem Type 1K-blocks Used Available Use% Mounted on
overlay overlay 102624184 2229468 95135556 3% /
tmpfs tmpfs 65536 0 65536 0% /dev
shm tmpfs 65536 0 65536 0% /dev/shm
/dev/vdb1 ext4 102624184 2229468 95135556 3% /etc/hosts
# ファイルシステムの詳細情報 (ext4)
# tune2fs: ext2、ext3、ext4ファイルシステムのパラメータを変更する
tune2fs -l /dev/vda1 | grep -E "Filesystem (state|created|features)"
Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Filesystem state: clean
Filesystem created: Sun Jul 27 11:29:37 2025
#マウント
##マウントとは
マウントとは、ストレージデバイスやファイルシステムをディレクトリツリーの特定の場所 (マウントポイント) に接続する操作。アンマウントするまでそのディレクトリ以下でデバイスのファイルシステムが利用できる。
##mount / umount コマンド
# ファイルシステム種別を明示してマウント
mkdir /mnt/data
mount -t ext4 /dev/vdb1 /mnt/data
# 現在のマウント状況を確認
mount
# または
findmnt
TARGET SOURCE FSTYPE OPTIONS
/ overlay overlay rw,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/840/fs:/var/lib/containerd/io.c
|-/mnt/data /dev/vdb1 ext4 rw,relatime
|-/proc proc proc rw,nosuid,nodev,noexec,relatime
|-/dev tmpfs tmpfs rw,nosuid,size=65536k,mode=755,inode64
| |-/dev/console devpts[/0] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666
| |-/dev/pts devpts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666
| |-/dev/mqueue mqueue mqueue rw,nosuid,nodev,noexec,relatime
| `-/dev/shm shm tmpfs rw,nosuid,nodev,noexec,relatime,size=65536k,inode64
|-/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime
| `-/sys/fs/cgroup cgroup cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot
|-/etc/resolv.conf /dev/vdb1[/docker/containers/5ba3be7065491808c8d893e298c8445c2a6f66071f86306e6122f03be2399693/resolv.conf]
| ext4 rw,relatime
|-/etc/hostname /dev/vdb1[/docker/containers/5ba3be7065491808c8d893e298c8445c2a6f66071f86306e6122f03be2399693/hostname]
| ext4 rw,relatime
`-/etc/hosts /dev/vdb1[/docker/containers/5ba3be7065491808c8d893e298c8445c2a6f66071f86306e6122f03be2399693/hosts]
ext4 rw,relatime
# アンマウント
umount /mnt/data
##/etc/fstab
/etc/fstab は起動時に自動マウントするファイルシステムの設定ファイル。
# <device> <mountpoint> <fstype> <options> <dump> <pass>
/dev/sda1 / ext4 defaults 0 1
/dev/sda2 none swap sw 0 0
UUID=xxxx-yyyy /mnt/data ext4 defaults,nofail 0 2
tmpfs /tmp tmpfs defaults,size=512m 0 0
| フィールド | 説明 |
|---|---|
| device | デバイスファイル・UUID・ラベルなど |
| mountpoint | マウント先ディレクトリ |
| fstype | ファイルシステム種別 |
| options | マウントオプション (defaults / ro / noexec など) |
| dump | dump コマンドのバックアップ対象 (通常 0) |
| pass | 起動時の fsck 実行順 (0=スキップ、1=最初、2=その後) |
# fstab の設定を実際にマウントせず検証
mount -a --fake
# fstab にある未マウントのエントリをすべてマウント
mount -a
##bind mount
bind mount は既存のディレクトリを別の場所にも見せる仕組みで、デバイスを伴わない点が通常のマウントと異なる。
# /data を /mnt/data にも見せる
mount --bind /data /mnt/data
# 読み取り専用で bind mount
mount --bind /data /mnt/data
mount -o remount,ro,bind /mnt/data
コンテナ技術での活用として、Docker はホストのディレクトリをコンテナ内に公開するためにバインドマウントを使用している。
# Docker でホストディレクトリをコンテナにマウント
docker run -v /host/data:/container/data ubuntu:22.04
# 内部的には Mount Namespace 内で bind mount が行われる
# コンテナの /container/data がホストの /host/data を指す
#特殊ファイルシステム
Linux ではディスクを持たない仮想的なファイルシステムが存在し、カーネル内部の情報をファイルとして公開している。
##procfs (/proc)
プロセス情報やカーネルパラメータをファイルとして公開する仮想ファイルシステム。
# 実行中プロセスの情報
ls /proc/$$
attr cmdline environ io loginuid mounts oom_adj projid_map setgroups statm timers
autogroup comm exe ksm_merging_pages map_files mountstats oom_score root smaps status timerslack_ns
auxv coredump_filter fd ksm_stat maps net oom_score_adj sched smaps_rollup syscall uid_map
cgroup cpuset fdinfo latency mem ns pagemap schedstat stack task wchan
clear_refs cwd gid_map limits mountinfo numa_maps personality sessionid stat timens_offsets
# プロセスのコマンドライン引数
cat /proc/$$/cmdline | tr '\0' ' '
/bin/bash root@5ba3be706549:~#
# プロセスのステータス
cat /proc/$$/status
Name: bash
State: S (sleeping)
Pid: 1
VmRSS: 2432 kB # 物理メモリ使用量
# CPU 情報
cat /proc/cpuinfo | head -5
processor : 0
BogoMIPS : 48.00
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp flagm2 frint
CPU implementer : 0x61
CPU architecture: 8
# メモリ情報
cat /proc/meminfo | head -5
MemTotal: 2006420 kB
MemFree: 1058944 kB
MemAvailable: 1745248 kB
Buffers: 208752 kB
Cached: 512312 kB
# カーネルパラメータ (sysctl の実体)
cat /proc/sys/net/ipv4/ip_forward
0
# 動的に変更
echo 1 > /proc/sys/net/ipv4/ip_forward
# または
sysctl -w net.ipv4.ip_forward=1
##sysfs (/sys)
デバイス・ドライバ・バスなどのカーネルオブジェクトを階層構造で公開する仮想ファイルシステム。/proc より整理された設計になっている。
# ネットワークインターフェース一覧
ls /sys/class/net/
eth0 lo
# インターフェースの状態確認
cat /sys/class/net/eth0/operstate
up
root@5ba3be706549:~# cat /sys/class/net/eth0/speed
10000 # Mbps
# ブロックデバイス情報
ls /sys/class/block/
loop0 loop1 loop2 loop3 loop4 loop5 loop6 loop7 vda vda1 vda15 vda16 vdb vdb1 vdc
cat /sys/class/block/vda/size # 512バイトセクタ単位
41943040
# cgroups も sysfs 経由で管理されている
ls /sys/fs/cgroup/
cgroup.controllers cgroup.procs cgroup.subtree_control memory.pressure ...
##tmpfs
RAM 上に展開されるファイルシステム。アクセスが非常に高速な反面、再起動するとデータは消える。
# tmpfs のマウント状況を確認
df -T | grep tmpfs
tmpfs tmpfs 65536 0 65536 0% /dev
shm tmpfs 65536 0 65536 0% /dev/shm
# 任意のサイズで tmpfs をマウント
mount -t tmpfs -o size=256m tmpfs /mnt/ramdisk
# /dev/shm は tmpfs で、プロセス間共有メモリに利用される
ls /dev/shm
# Docker コンテナで tmpfs を使う
docker run --tmpfs /tmp:size=128m ubuntu:22.04
#ファイルディスクリプタ
##ファイルディスクリプタとは
ファイルディスクリプタ (fd) は、プロセスがオープンしたファイルやソケット・パイプなどを識別するための非負整数。プロセスごとに「オープンファイルテーブル」が管理されており、fd はそのテーブルへのインデックスとして機能する。
システムコール open() が成功するとカーネルは fd を返し、以降 read() や write() の第一引数として fd を渡すことで操作できる。
##標準入出力
プロセス起動時に以下の 3 つの fd が自動的に割り当てられる。
| fd | 名前 | 用途 |
|---|---|---|
| 0 | stdin (標準入力) | キーボード入力など |
| 1 | stdout (標準出力) | 通常の出力 |
| 2 | stderr (標準エラー) | エラーメッセージ |
##/proc//fd での確認
# 現在のシェルがオープンしているファイルディスクリプタ
root@5ba3be706549:~# ls -la /proc/$$/fd
total 0
dr-x------ 2 root root 4 Apr 1 13:40 .
dr-xr-xr-x 9 root root 0 Mar 28 01:47 ..
lrwx------ 1 root root 64 Apr 1 13:40 0 -> /dev/pts/0 # stdin
lrwx------ 1 root root 64 Apr 1 13:40 1 -> /dev/pts/0 # stdout
lrwx------ 1 root root 64 Apr 1 13:40 2 -> /dev/pts/0 # stderr
lrwx------ 1 root root 64 Apr 1 14:24 255 -> /dev/pts/0 # bash 内部用
# ファイルをオープンして fd を確認
exec 3< /etc/hostname # fd 3 で /etc/hostname を読み取り専用で開く
ls -la /proc/$$/fd/3
lrwx------ 1 user user 64 Mar 28 10:00 3 -> /etc/hostname
exec 3<&- # fd 3 を閉じる
# lsof で特定プロセスの fd を確認
lsof -p $$
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 1234 user cwd DIR 8,1 4096 524288 /home/user
bash 1234 user 0u CHR 136,0 0t0 3 /dev/pts/0
bash 1234 user 1u CHR 136,0 0t0 3 /dev/pts/0
bash 1234 user 2u CHR 136,0 0t0 3 /dev/pts/0
##リダイレクトとパイプ
リダイレクトやパイプはファイルディスクリプタを付け替える操作として実現される。
# stdout (fd 1) をファイルにリダイレクト
command > output.txt
# 内部的には fd 1 を output.txt の fd に付け替えている
# stderr (fd 2) を stdout (fd 1) にリダイレクト
command 2>&1
# stdout と stderr を同じファイルへ
command > output.txt 2>&1
# bash 4.0 以降の短縮記法
command &> output.txt
# パイプ: 左コマンドの stdout を右コマンドの stdin に接続
ls -la | grep ".txt"
# 内部的には pipe(2) システムコールで無名パイプを作成し
# ls の fd 1 と grep の fd 0 を繋いでいる
# /dev/null に捨てる (fd を /dev/null に向ける)
command > /dev/null 2>&1
##プロセスごとの fd 上限
1 プロセスが同時にオープンできる fd 数には上限がある。
# ソフトリミット (プロセスが自分で変更できる上限)
ulimit -n
1024
# ハードリミット (root のみ変更可能な上限)
ulimit -Hn
1048576
# 上限を変更 (セッション内のみ有効)
ulimit -n 4096
# システム全体の上限
cat /proc/sys/fs/file-max
9223372036854775807
# 現在システム全体でオープンされている fd 数
cat /proc/sys/fs/file-nr
6144 0 9223372036854775807
# 使用中 解放済み 上限
Web サーバーやデータベースなどの多接続サービスでは fd の枯渇がボトルネックになることがあるため、ulimit -n や /etc/security/limits.conf で適切に調整することが重要。