// linux fundamentals — module 03
Module 3 — Everything Is a File
Thesis: Unix's most famous idea: nearly everything — documents, directories, disks, running processes, even the random-number generator — is presented as a file in one unified tree. Learn the map of that tree, and then learn what a "file" actually is underneath (spoiler: a file and its name are two different things).
Prerequisite: Module 2 — The Kernel — you've already used virtual files in /proc without being told what made them possible. Now you find out.
3.1 Why "everything is a file" is a design, not a slogan
Recall the kernel's abstraction job from Module 0: hide hardware chaos behind uniform ideas. Unix's answer was radical uniformity — define one interface (open, read, write, close, the syscalls from Module 2) and make as many things as possible speak it:
- A document is a file. Obviously.
- A directory is a file whose content is a list of names (§3.4 makes this precise and surprising).
- A disk is a file:
/dev/sda— reading it gives you the raw bytes of the drive. - Your terminal is a file:
/dev/tty— writing to it prints on your screen. - Randomness is a file:
/dev/urandom— reading it yields random bytes. - Kernel state is files:
/proc,/sys(Module 2). - Even network connections are file descriptors once opened (Module 5).
The payoff is compositional: every tool that reads and writes files automatically works on all of these things. The Unix philosophy (§0.6) is only possible because of this substrate — text streams can be universal because "file" already is.
3.2 The map: the Filesystem Hierarchy Standard
Linux distros agree (by a convention called the FHS — see man 7 hier) on what the top-level directories mean. This is the map of every Linux system you'll ever touch. Learn it as purposes, not trivia:
Programs and libraries:
/bin,/usr/bin— programs ("binaries") for everyone. On modern distros/binis a symlink to/usr/bin(a historical merger — the split dates from a 1970s disk being full, and it fossilized for fifty years; a taste of why NixOS's rethink is refreshing)./sbin,/usr/sbin— system-administration programs (mostly for root; often merged intobinnow too)./usr/lib(and/lib) — shared libraries, code used by many programs (Module 9's main stage), plus support files./usr/local— a parallel bin/lib tree for software you install by hand, kept out of the package manager's territory. The existence of this "yours vs. the package manager's" split is a first hint of the turf problems Module 9 dissects./opt— big self-contained third-party apps that bring their whole directory with them.
Configuration and variable data:
/etc— system-wide configuration, in plain text. The name is literally "et cetera" (it was Unix's junk drawer); think "Editable Text Config." You'll spend Modules 4–8 reading files in here./var— variable data: things that grow and change./var/log(logs — you'll live here in Module 7),/var/cache,/var/lib(databases and state kept by services)./home— user home directories./root— the root user's home (not in/home, so root can log in even if/homelives on a separate, possibly-broken disk — a mount-related design choice you can fully appreciate by §3.6)./tmp— scratch space for everyone, wiped on reboot (usually RAM-backed — §3.7). Anything may delete anything's leftovers here; there's a permissions trick making that safe, revealed in Module 4.
The kernel's territory:
/dev— device files (§3.3)./proc,/sys— the kernel-state windows from Module 2./boot— the kernel image itself and the bootloader's files (Module 6's stage)./run— runtime state of the current boot (PID files, sockets), RAM-backed, empty at each boot.
Mount scaffolding: — /mnt (manual, temporary mounts) and /media (auto-mounted USB sticks etc.); both are empty stages for §3.6.
Don't memorize this list tonight; instead, from now on, never see a path without knowing which of these territories it's in. That habit is the actual skill.
3.3 The seven file types
Every file is exactly one of seven types, and ls -l shows the type as the first character of each line:
| Char | Type | What it is |
|---|---|---|
- |
regular file | bytes: text, images, programs |
d |
directory | a list of names (§3.4) |
l |
symbolic link | a file containing a path to another file (§3.5) |
c |
character device | a device you read/write as a byte stream: terminals, /dev/urandom |
b |
block device | a device you read/write in blocks with random access: disks, /dev/sda |
s |
socket | a rendezvous point for inter-process communication (Module 7's systemd uses these heavily) |
p |
named pipe (FIFO) | a shell pipe (Module 5) given a filename |
The two device types deserve one more sentence: a device file carries no data at all — it's a labeled doorway. Reading or writing it invokes a driver in the kernel (Module 2's modules, surfacing here). /dev/sda is not a copy of your disk; it is your disk, addressed through the file interface.
Two famous character devices to know: /dev/null — a bottomless discard bin (write anything, it vanishes; read it, instant end-of-file; Module 5 makes 2>/dev/null a daily idiom) — and /dev/zero (endless zero bytes).
3.4 Inodes: a file's data and its name are separate things
Here is the deepest idea in this module. On disk, a filesystem stores two kinds of records:
- Inodes. One per file. The inode holds everything about the file — size, owner, permissions (Module 4 reads these), timestamps, and where on disk the content lives. Everything, that is, except the name. Inodes are numbered; run
ls -iand see each file's inode number. - Directories. A directory is just a table of
name → inode numberpairs. That's all a directory is.
So "a file" is really an inode plus content, and a filename is merely an entry in some directory's table pointing at that inode. The consequences are immediate and explain things you've already used:
mvis instant even on a huge file (within one filesystem): it rewrites a directory entry — the name — and never touches the data. This is why Module 1 said rename and move are the same operation.- A file can have several names. Nothing prevents two directory entries — even in different directories — from pointing at the same inode. Each such name is a hard link, and they are perfectly equal: no original, no copy, one file with two names. The inode keeps a link count (visible as the number right after permissions in
ls -l). - "Deleting a file" is not a thing — deleting a name is.
rmperforms the syscallunlink: remove one directory entry, decrement the link count. Content is freed only when the count hits zero and no process holds it open (Module 5's file descriptors keep files alive — this is why you can delete a log file a server has open and the disk space doesn't come back until the server closes or restarts it: a legendary sysadmin gotcha you now understand in advance).
Why is every directory's link count at least 2? Because every directory contains the entries . (itself) and its children contain .. — Module 1's dot-names turn out to be literal hard links in the table. The tree is stitched together with the same mechanism.
3.5 Symlinks: the other kind of link
A symbolic link (symlink) is a genuinely different beast: a tiny file (own inode, type l) whose content is a path, created with ln -s target linkname. When you open it, the kernel reads the path and transparently follows it. ls -l shows the arrow: mylink -> /some/target.
Hard link vs. symlink — the contrasts that matter:
| Hard link | Symlink | |
|---|---|---|
| What it is | another name for the same inode | a separate file containing a path |
| If the target is deleted | data survives — you still hold a name for it | link dangles, pointing at nothing (opens fail) |
| Across filesystems / disks | impossible (inode numbers are per-filesystem) | fine — it's just a path string |
| Point at a directory | forbidden (would tangle the tree into cycles) | fine — used everywhere |
| Visibility | invisible — indistinguishable from any name | announces itself (l, the arrow) |
Symlinks are the duct tape of Unix system design, and you've already stepped over them: /bin → /usr/bin (§3.2), /proc/$$/cwd from Lab 2 (a symlink the kernel fabricates), and version indirections like libfoo.so.1 → libfoo.so.1.4.2 all through /usr/lib (mechanism explained in Module 9). Check file /bin and watch it say so.
3.6 Mounting: how many disks become one tree
Module 1 said Linux has one tree, no drive letters. Mounting is the mechanism: to make a filesystem (a disk partition, USB stick, network share…) accessible, you graft it onto an existing directory — the mount point. From then on, paths that descend into that directory transparently descend into the other filesystem. Anything previously inside the mount point is hidden (not harmed) until unmount.
mount /dev/sdb1 /mnt # graft the USB stick's filesystem at /mnt
ls /mnt # now shows the stick's contents
umount /mnt # detach (note: umount, no 'n')
The running system is a stack of mounts: run findmnt and see the tree — the real root filesystem at /, with /proc, /sys, /dev, /run, /tmp, maybe /boot and /home, each a separate filesystem grafted at its point. Mount facts worth owning:
- A "filesystem" here means a format for arranging inodes and data on a device —
ext4(the Linux default),xfs,btrfs,vfat(USB sticks, and the EFI partition in Module 6),ntfs(Windows). The kernel speaks each via a driver (Module 2 again). /etc/fstabis the plain-text table of what to mount where at boot: one line per mount — device, mount point, type, options. Read yours in the lab. (On modern systems, systemd actually parses fstab and turns each line into a mount unit — Module 7 will close that loop.)- Devices get names like
/dev/sda1(first partition of first disk), but those names depend on detection order — so fstab prefers UUIDs, unique IDs stamped into each filesystem (lsblk -fshows them). Same "names are separate from identity" moral as inodes, at disk scale. umountcan refuse: "target is busy" means some process has a file open (or a cwd) inside it. You have the Module 2 skills to find the culprit (lsoforfuserare the purpose-built tools).
3.7 Virtual and memory-backed filesystems
Now the §3.2 map fully clicks. Not every mounted filesystem is backed by a device:
procandsysfs— content fabricated by the kernel per-read (Module 2). They're mounted at/procand/sys— see them infindmnt— the same grafting mechanism, just with no disk behind it.tmpfs— a real, read-write filesystem living purely in RAM: fast, and gone at power-off. This is what's mounted at/tmpand/run— which is why those directories are empty each boot.devtmpfsat/devis its cousin, auto-populated with device files as the kernel detects hardware.
So the single tree is a composite: a disk-backed trunk, kernel-window grafts, RAM-backed scratch spaces — all speaking the one file interface from §3.1. That's the whole trick, and you now know every part of it.
3.8 → NixOS
This module is the single biggest "aha" for NixOS. A conventional distro scatters software across the FHS — every package's binaries commingled in /usr/bin, every package's libraries commingled in /usr/lib. NixOS deliberately abandons that. Almost everything lives in /nix/store, one isolated directory per package version, named by hash:
/nix/store/9an9ijxk2r51...-ripgrep-14.1.0/bin/rg
And the familiar world is reconstructed as symlinks: /run/current-system → a store path describing your whole OS; your $PATH entries → symlink forests merging the bin/ directories of exactly your declared packages; even /etc files are largely symlinks into the store. Switching system generations (Module 6 will explain generations) is mostly repointing one symlink — which is why it's atomic and instantly reversible.
Every mechanism in that design — the FHS it departs from, the symlinks it's built from, the mounts underneath, names-vs-inodes making symlink swaps atomic — is this module. If §3.4 and §3.5 are solid, NixOS's layout will read as elegant. If they're wobbly, it reads as alien. That's why this mastery check matters more than any other.
Lab 3
Walk the map. Run
ls -l /and, for every entry, say its purpose out loud from §3.2 before checking. Note which entries are symlinks (l, arrow) — likely/bin,/sbin,/lib. Then skimman 7 hierand find one directory this module didn't cover.Spot all seven types. Find one of each in the wild and verify the first
ls -lcharacter: a regular file (anything in/etc), a directory, a symlink (/bin), a block device (ls -l /dev/sd*or/dev/nvme*), a character device (ls -l /dev/null /dev/tty), a socket (ls -l /run | grep ^s), a named pipe (create one:mkfifo mypipe; ls -l mypipe; rm mypipe).Prove that data and names are separate. In a scratch directory:
echo "the data" > original ln original hardlink ln -s original symlink ls -liConfirm:
originalandhardlinkshare an inode number and show link count 2;symlinkhas its own inode. Nowrm original, thencat hardlinkandcat symlink. Explain both results with §3.4/§3.5. (The data survived; the symlink dangles.)Watch a rename not touch data. Create a large file (
head -c 500M /dev/urandom > big— reading randomness from a file, §3.1), thentime mv big renamed— instant, and now you know why. Clean up withrm renamed. Bonus:time cpit first and compare.Read the mount stack. Run
findmnt | less. Identify: the root filesystem and its type (ext4? btrfs?), which mounts areproc/sysfs, and which aretmpfs(expect/tmp,/run). Then read/etc/fstaband match its lines to live mounts. Runlsblk -fand find your root partition's UUID in both places.Mount something yourself (best on a VM; needs root and any USB stick). Plug in the stick,
lsblkto find it (say,/dev/sdb1), then:sudo mount /dev/sdb1 /mnt findmnt /mnt sudo umount /mntThen remount it,
cd /mnt, and tryumountagain — enjoy "target is busy,"cdaway, retry. You've now caused and cured the classic mount error.Play with device files. Run
echo "hello" > /dev/null(gone forever),head -c 16 /dev/urandom | xxd(16 random bytes, hex-dumped), andecho "surprise" > /dev/tty(printed — you wrote to the terminal device).
✅ Mastery Check — do not proceed until true
Answer out loud, without notes:
- For each of:
/etc,/var/log,/usr/bin,/dev,/proc,/tmp,/boot,/run— what lives there, and (for the last four) what's backing it: disk, RAM, or kernel fabrication? - Name the seven file types and how
ls -lmarks each. - What does an inode contain, and what one thing does it not contain? Where do filenames actually live?
- Explain why
mvis instant on a 500 MB file butcpisn't. rma file and the disk space doesn't return. Give two distinct explanations (link count; open descriptor).- Hard link vs. symlink: four differences, including what happens to each when the target is deleted, and which can cross filesystems.
- What does mounting do, exactly? What is
/etc/fstab, and why does it use UUIDs instead of/dev/sda1? - What is
tmpfs, and why are/tmpand/runempty after every reboot?
And perform cold:
- Create a hard link and a symlink and demonstrate, with
ls -liand inode numbers, what each is. - Read
findmntoutput and identify what's mounted where and from what. - Mount and cleanly unmount a USB stick, resolving a "target is busy."
When all of that is effortless: Module 4 — Users, Groups, and Permissions