Linux · lesson 1 of 7
Beginner
2Intermediate
5- 2 Permission Bits, Ownership, and Why passwd Needs setuid
- 3 A Zombie Process That Never Gets Reaped — and the PID 1 That Fixes It
- 4 systemd Service Supervision: Restart Limits and Why They Trip
- 5 Linux Networking: Routes, Interfaces, and Bind Addresses
- 7 Inodes: The Filesystem Is Full, but df -h Says 1% Used
Also part of Linux
7- + Bash Text Processing from Bash
- + Bash Process Management from Bash
- + Bash System Information from Bash
- + Cron and Scheduling in Bash from Bash
- + Bash File Operations from Bash
- + Reading Linux SSH Auth Logs: Failed Logins You Generate Yourself from Defensive Security
- + Host Firewalls with nftables: DROP Times Out, REJECT Refuses from Networking
The Linux Filesystem Hierarchy, and Why /bin Is a Symlink
ls -la / shows /bin, /lib, and /sbin as symlinks into /usr — not historical clutter, but the real result of a filesystem-layout change most distros made years ago.
By SkillByExample · Updated
ls -la / on a modern Linux system shows several directories that aren’t really directories —
and that’s not an accident.
The real filesystem hierarchy
docker run -d --name linux-lesson --privileged ubuntu:22.04 sleep infinity
docker exec linux-lesson ls -la /
total 72
drwxr-xr-x 1 root root 4096 Sep 14 05:55 .
drwxr-xr-x 1 root root 4096 Sep 14 05:55 ..
-rwxr-xr-x 1 root root 0 Sep 14 05:55 .dockerenv
lrwxrwxrwx 1 root root 7 Sep 1 20:33 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Apr 18 2022 boot
drwxr-xr-x 11 root root 3140 Sep 14 05:55 dev
drwxr-xr-x 1 root root 4096 Sep 14 05:55 etc
drwxr-xr-x 2 root root 4096 Apr 18 2022 home
lrwxrwxrwx 1 root root 7 Sep 1 20:33 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Sep 1 20:33 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 Sep 1 20:33 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 Sep 1 20:33 libx32 -> usr/libx32
drwxr-xr-x 2 root root 4096 Sep 1 20:33 media
drwxr-xr-x 2 root root 4096 Sep 1 20:33 mnt
drwxr-xr-x 2 root root 4096 Sep 1 20:33 opt
dr-xr-xr-x 301 root root 0 Sep 14 05:55 proc
drwx------ 2 root root 4096 Sep 1 20:42 root
drwxr-xr-x 1 root root 4096 Sep 14 05:55 run
lrwxrwxrwx 1 root root 8 Sep 1 20:33 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Sep 1 20:33 srv
dr-xr-xr-x 11 root root 0 Sep 14 05:55 sys
drwxrwxrwt 1 root root 4096 Sep 14 05:55 tmp
drwxr-xr-x 1 root root 4096 Sep 1 20:33 usr
drwxr-xr-x 1 root root 4096 Sep 1 20:42 var
Look at the l at the start of the bin, lib, lib32, lib64, libx32, and sbin lines
— every one of those is a symlink, not a real directory. bin -> usr/bin means every binary
you’d expect to find at /bin/ls actually lives at /usr/bin/ls; the shorter path is kept
only so decades of scripts and muscle memory that assume /bin/bash exists don’t break. This
is the “usr merge” that Debian, Ubuntu, Fedora, and Arch have all adopted — a single real
location for system binaries, which simplifies read-only root filesystems and atomic updates.
Identifying the distro and kernel
docker exec linux-lesson cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
/etc/os-release is a plain key-value text file every modern distro ships specifically so
scripts can parse it reliably — ID and VERSION_ID are the two fields worth grepping for in
an automated setup script.
docker exec linux-lesson uname -a
Linux cf95086ff556 5.15.153.1-microsoft-standard-WSL2 #1 SMP Fri Mar 29 23:14:13 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
uname reports on the kernel, not the distro — the -microsoft-standard-WSL2 suffix here
gives away that this particular kernel is Windows Subsystem for Linux’s, regardless of which
distro’s userland (Ubuntu, in this case) is running on top of it. Distro identity and kernel
identity are genuinely separate questions, answered by separate commands.
/proc: a filesystem the kernel makes up on the fly
docker exec linux-lesson cat /proc/version
Linux version 5.15.153.1-microsoft-standard-WSL2 (root@941d701f84f1) (gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37) #1 SMP Fri Mar 29 23:14:13 UTC 2024
Nothing under /proc is stored on disk — it’s a virtual filesystem the kernel generates when
you read it, which is why the exact same cat /proc/version returns live information rather
than a cached file’s contents. This matters throughout the rest of this track: /proc is how
later lessons inspect real running processes without needing any special tooling.
Practice
1. Confirm the kernel release alone, without the rest of uname -a's output.
docker exec linux-lesson uname -r
5.15.153.1-microsoft-standard-WSL2
-r alone is what a script checking a minimum kernel version should parse — grepping the full
-a output for a version number is more fragile than asking for exactly the field needed.
2. Explain the permission string on /tmp and what makes it different from an ordinary world-writable directory.
Looking back at the ls -la / output: drwxrwxrwt on /tmp. Every user can create files
there (rwx for owner, group, and others) — but the final t is the sticky bit. It means
only a file’s own owner (or root) can delete or rename it, even though anyone can write into
the directory. Without the sticky bit, any user could delete any other user’s temporary files.
3. Read a live value from /proc that changes between calls.
docker exec linux-lesson cat /proc/uptime
sleep 1
docker exec linux-lesson cat /proc/uptime
288973.71 3451867.57
288975.27 3451886.00
Two back-to-back reads of the same “file” return different numbers — the first field is
seconds since boot, the second is total idle time summed across all CPUs. Nothing was written
to disk between the two calls; the kernel computed a fresh answer both times. Notice the
number itself is large (over 3 days) even though this container just started: a container
doesn’t get its own kernel, so /proc/uptime here reports the host kernel’s uptime, not
how long the container has been running — one more reminder that a container is an isolated
process view on a shared kernel, not a separate machine.
Next: permissions and ownership — the bit-level mechanics behind a real permission-denied error.