Filesystem
Linux's filesystem is one tree — a single root / from which every disk, every device, every process, every kernel interface hangs. There are no drive letters; a second disk doesn't become D: — it mounts into the tree at any point you choose. And beneath the tree: inodes — the kernel's real file objects, which filenames merely point at. Learn the tree and the inodes and you've learned the filesystem; everything else is elaboration.
The one-tree rule
A disk, a CD-ROM, a USB stick, a network share, a directory of the kernel's internals — all appear as paths under /. The filesystem namespace is unified even though the storage behind it is scattered across devices:
df -hT # which device backs which path — the tree's true plumbing
mount # every currently-mounted filesystem
findmnt # the mount tree, visualized
This is what mount actually does — graft a device onto a path. Unmount it and the path is empty again; the tree is the interface, the mounts are the plumbing.
The FHS map
The Filesystem Hierarchy Standard names every top-level directory:
| Path | Holds | Notes |
|---|---|---|
/bin | essential user binaries | → /usr/bin (merged-usr on modern systems) |
/boot | kernel, initramfs, bootloader | often its own partition |
/dev | device files | devtmpfs — populated by the kernel itself |
/etc | system configuration | all text, all yours |
/home | user homes | |
/lib | essential shared libraries | → /usr/lib |
/media | auto-mount point for removable media | |
/mnt | manual temporary mounts | LFS's staging ground |
/opt | third-party software | |
/proc | process + kernel state | virtual — kernel-composed, not on disk |
/root | root's home | |
/run | runtime state | tmpfs — dies on boot |
/sbin | system binaries | → /usr/sbin |
/srv | site-served data | |
/sys | devices/drivers/kernel objects | sysfs — virtual |
/tmp | temporary files | usually tmpfs; world-writable + sticky |
/usr | the userland's bulk | bin, lib, share — read-only-able |
/var | variable data | logs, spool, caches |
The modern merged-usr layout makes /bin, /sbin, /lib symlinks into /usr — the split is historical (early / was a rescue root; /usr came later); modern systems unify them.
Inodes — what a file actually is
A filename is not a file — it's a directory entry pointing at an inode. The inode holds everything: size, owner, permissions, timestamps, and the block pointers to the data. The name is just one of possibly several links to it:
ls -i file # the inode number
stat file # the inode's full record: times, links, blocks
df -i # inode exhaustion — a full FS can have blocks free
This architecture explains three Unix truths:
- Hard links —
ln a bmakes a second name for the same inode; no "original" exists, only a refcount. Delete one name and the file persists until the last name goes. - Symlinks —
ln -smakes a new inode whose contents are a path — a pointer by name, not by number; it dangles if the target dies. - Moving within a filesystem is free —
mvrewrites a directory entry; the inode and data never move.mvacross filesystems is copy+delete.
The FHS map — the deep version
| Path | Holds | Notes |
|---|---|---|
/bin →/usr/bin | user binaries | ls cp mv bash — merged-usr symlink |
/boot | kernel + initramfs + bootloader | its own partition often; vmlinuz-* initrd grub/ |
/dev | device nodes | devtmpfs — kernel-populated: null zero urandom tty sda* pts/ |
/etc | all configuration | fstab passwd shadow ssh/ sysconfig/ hosts resolv.conf |
/home | user homes | ~alice etc |
/lib →/usr/lib | shared libs | libc.so the ld.so linker |
/lib64 | 64-bit libs | x86_64 split |
/media | automount point | CDs, USB sticks |
/mnt | manual mounts | LFS's $LFS |
/opt | third-party | self-contained packages |
/proc | kernel+process state | virtual — cpuinfo meminfo PID/ |
/root | root's home | |
/run | runtime state | tmpfs — run/user/ run/lock |
/sbin →/usr/sbin | admin binaries | fdisk mkfs useradd init |
/srv | site-served data | |
/sys | device/driver objects | sysfs — class/ block/ bus/ |
/tmp | temp | world-writable sticky 1777, cleared on boot |
/usr | the userland bulk | bin lib share include local src — can be read-only |
/usr/local | your additions | software you installed yourself |
/var | variable data | log spool cache mail lib |
/var/log | the logs | messages syslog auth.log |
/var/lib | state | dpkg status, rpm db |
/var/spool | queues | mail, cron, printing |
The merged-usr story: historically / was a minimal rescue root; /usr came later with the real userland — modern systems merge /bin /sbin /lib → /usr/* symlinks. The FHS exists so every Linux looks the same — a file lives where you expect.
The special file types — the complete set
ls -l
- regular file
d directory
l symlink — a path in an inode
c character device — streams: tty, null, urandom, serial
b block device — random-access: sda, loop
p named pipe — FIFO — mkfifo
s unix socket — local IPC
mkfifo myfifo; echo hi > myfifo & cat myfifo # a file that's a queue
mknod /dev/foo b 8 32 # a device node by hand
Devices as files
/dev holds not programs but device nodes — files that open kernel drivers:
ls -l /dev | head
# crw-rw-rw- 1 root root 1, 3 null <- c = character, 1,3 = major,minor
# brw-rw---- 1 root disk 8, 0 sda <- b = block device
echo hi > /dev/null # a write to the kernel's void
cat /dev/urandom | head -c16 | xxd # reading entropy as a file
Major number = which driver; minor = which instance. /dev/null, /dev/zero, /dev/urandom, /dev/full, tty devices, disks — all files, all opened with the same open() syscall. This is "everything is a file" made literal.
Virtual filesystems — the kernel exposes itself
Three pseudo-filesystems are kernel interfaces wearing file costumes:
/proc— every process as a directory; kernel state as files:cat /proc/cpuinfo,cat /proc/meminfo,ls /proc/self,echo 1 > /proc/sys/net/ipv4/ip_forwardto configure the kernel by writing a file./sys(sysfs) — devices, drivers, buses as a browsable object tree./dev(devtmpfs) — populated automatically as hardware is detected.
None of it lives on disk — the kernel composes it on read. It's the purest "everything is a file" — including the kernel's own mind.
Filesystem types
| FS | Purpose |
|---|---|
| ext4 | The boring correct default — journaled, everywhere |
| xfs | Large files, large filesystems |
| btrfs | CoW, snapshots, checksums |
| tmpfs | RAM-backed — /run, /tmp |
| vfat | The ESP, USB sticks, interop |
| iso9660 | CD images |
| nfs, cifs | Network filesystems |
| proc, sysfs, devtmpfs, cgroup2 | The kernel interfaces |
Permissions on the tree
Every inode carries owner, group, and mode — the rwxrwxrwx triad ×3 (detailed in Users and permissions). On filesystems: mount options like noexec, nosuid, nodev, ro constrain what inodes permit — /proc and /sys mount nosuid,nodev,noexec for good reason.
/proc — the kernel's process window, deeper
cat /proc/cpuinfo # every core's model, flags, mhz
cat /proc/meminfo # the memory ledger — MemFree vs Buffers/Cached
cat /proc/loadavg # load averages + running/total + last pid
cat /proc/uptime # uptime + idle seconds
cat /proc/mounts # the real mount table
cat /proc/filesystems # what filesystems the kernel knows
cat /proc/partitions # the block devices
cat /proc/version # kernel + compiler
cat /proc/cmdline # the boot params
cat /proc/interrupts # irq traffic
cat /proc/net/tcp # the sockets, raw
cat /proc/sys/kernel/hostname # a writable kernel knob
ls /proc/self/ # YOUR reading process as files
Every process is /proc/PID/ — status, cmdline, environ, fd/, maps, io, limits, cwd→real path, exe→the binary. /proc/sys/ is the sysctl tree — writable knobs. /proc is pure kernel-composed — cat runs code, not disk I/O.
/sys — the device object tree
ls /sys/class/net/ # every NIC as a directory
cat /sys/class/net/eth0/address # its MAC
ls /sys/block/ # every block device
cat /sys/block/sda/queue/scheduler # io scheduler — live-tunable
ls /sys/bus/usb/devices/ # the USB tree
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
echo performance > /sys/.../scaling_governor # tune it
udevadm info -a /dev/sda # the device's full sysfs record
/sys is sysfs — the kernel's devices, drivers, buses, and classes as a browsable hierarchy — the same objects udev watches for hotplug.
Mount options — the flags that harden
mount -o ro,noexec,nosuid,nodev /dev/sda3 /mnt
# ro read-only
# noexec no binaries execute — /tmp, data partitions
# nosuid setuid bits disabled
# nodev no device files honored
# noatime don't update access times — the performance flag
# relatime the modern atime compromise (default)
# sync no write caching
# _netdev wait for network
# nofail don't fail boot if absent
# x-systemd.automount lazy-mount on first access
# bind mount a dir onto another path
noexec/nosuid/nodev/ro on /tmp, /proc, /sys, data mounts are the hardening layer the fstab options encode.
The file types — the leading char decoded
ls -l
# - regular file
# d directory
# l symlink
# c character device — streams (tty, null)
# b block device — random access (disks)
# p named pipe — FIFO
# s unix socket
mkfifo myfifo # a named pipe — a file that's a queue
Practical: find and friends
find /etc -name '*.conf' # name match
find / -type f -size +100M 2>/dev/null # the space hogs
find . -mtime -7 # changed this week
find / -xdev -name target # stay on one filesystem
find . -type l -xtype l # broken symlinks
du -sh /var/* | sort -rh | head # where the space went
Exercises
findmnt— draw the actual mount tree of your system.- Create a hard link and a symlink;
statboth; explain the inode difference. cat /proc/meminfo,ls /sys/class/net— browse the kernel's mind.df -i— could your filesystem hold a million empty files?- Mount a USB stick manually —
mount /dev/sdX1 /mnt— and unmount.
Timeline
| Concept | What it is |
|---|---|
| One tree | Every device grafts into / |
| Inodes | The real files; names are links |
| /dev | Devices as files |
| /proc, /sys | The kernel exposing itself |
| FHS | The standard map of / |