Computing Foundations › Inside the Machine › Day 6
Hands-on lab — Day 6: Operating Systems: What They Do and Why
- ← Back to the Day 6 lesson
- Open the hands-on files on GitHub — clone or download them from the public labs repository
- Local path in your clone:
labs/sections/computing-foundations/day-006-operating-systems-what-they-do-and/
Commands
Setup
cd labs/sections/computing-foundations/day-006-operating-systems-what-they-do-and Run
bash examples/explore_os.sh
bash starter/explore_os.sh Test
bash tests/run_tests.sh File tree
examples/explore_os.sh expected-output/FIELDS.md expected-output/sample-macos.txt metadata.yml README.md requirements/README.md security.md starter/explore_os.sh starter/os-profile-worksheet.md tests/run_tests.sh troubleshooting.md
Lab README
Day 006 lab — Meet Your Operating System
Lesson
- Lesson title: Operating Systems: What They Do and Why
- Day number: 6 of 365
- Lesson article: https://ai-roadmap-365.github.io/day-006-operating-systems-what-they-do-and
- Lab files: everything you need is in this directory — follow “How to run” below.
- Browse the course locally: from the repository root, this lab also appears in the course website at
/labs/day-006-operating-systems-what-they-do-andwhen the site is running.
Purpose
Day 6's lesson explained what an operating system does — managing processes, memory, files, devices, and permissions behind a system-call boundary. This lab makes that concrete: you interrogate your own OS from the terminal — kernel, uptime, identity, shell, filesystems, and the processes it is juggling right now — and add an OS layer to the machine profile you started on Day 1.
Learning objectives
- Read your kernel name and version and explain what the kernel is.
- Identify your login shell and your user identity.
- List your mounted filesystems and your top memory-consuming processes.
- Explain how the OS sits between your programs and the hardware.
Prerequisites
- The Day 6 lesson.
- The Day 1 machine profile (you extend it with OS-layer facts).
- A terminal; no programming experience required.
Supported operating systems
- macOS — fully supported (Darwin).
- Linux — fully supported, including WSL.
- Windows — run inside WSL (the script works unchanged), or read the
equivalent facts with PowerShell
Get-ComputerInfoand fill the worksheet by hand.
Hardware requirements
Any computer. The lab only reads system information; it writes nothing and needs no special hardware.
Required software
bash plus standard OS utilities (uname, uptime, whoami, ps, df).
All preinstalled — see requirements/README.md.
Free and open-source options
Everything here is free and ships with your OS. No accounts, keys, or purchases.
Installation
None. Change into this directory:
cd labs/sections/computing-foundations/day-006-operating-systems-what-they-do-and
File structure
day-006-.../
├── README.md
├── metadata.yml
├── starter/
│ ├── explore_os.sh ← YOUR working file (numbered exercises)
│ └── os-profile-worksheet.md ← record your machine's OS-layer facts
├── examples/
│ └── explore_os.sh ← completed reference implementation
├── tests/
│ └── run_tests.sh
├── expected-output/
│ ├── sample-macos.txt ← real captured run
│ └── FIELDS.md ← required fields on every platform
├── requirements/README.md
├── troubleshooting.md
└── security.md
How to run
## 1. See the finished tour first
bash examples/explore_os.sh
## 2. Complete the exercises in the starter, then run it
bash starter/explore_os.sh
## 3. Check your work
bash tests/run_tests.sh
What the commands do
bash examples/explore_os.sh— prints your kernel line (uname -a), uptime, user identity, login shell, a filesystem summary (df -h), your top five memory-consuming processes (ps), and where the kernel lives on disk — labelled per OS.bash starter/explore_os.sh— the same tour with numbered exercises for you to complete; each names the exact command.bash tests/run_tests.sh— runs the reference (and your starter) and checks every required section prints and the script exits cleanly.
Expected output
See expected-output/sample-macos.txt
for a real captured run and expected-output/FIELDS.md
for the fields required on every platform. Your kernel version, shell, and
process list will differ.
Validation steps
bash starter/explore_os.shexits with no error.- Every section header prints (Kernel, Uptime, Identity, Shell, Filesystems, Top processes).
- Your kernel name matches your OS (Darwin on macOS, Linux on Linux/WSL).
- The tests pass.
Tests
bash tests/run_tests.sh
Expected final line: 29 checks, 0 failure(s). (exit 0 on success,
non-zero on any failure).
Cleanup
Nothing to clean up — the scripts only read system information. Reset your
edited starter with git checkout -- starter/explore_os.sh.
Troubleshooting
See troubleshooting.md — ps column differences,
WSL notes, and reading the kernel path per platform.
Security notes
See security.md. The scripts are read-only, need no sudo,
and make no network calls; note that ps can show other users' process
names, so avoid sharing full output from shared machines.
Extension exercises
- Add the number of running processes (
ps -ax | wc -l) to your worksheet and compare it with your CPU core count from Day 1. - Look up one unfamiliar process from the top-memory list and record what it is.
- On Linux, print your distribution's
PRETTY_NAMEfrom/etc/os-release; on macOS, print the Darwin version and map it to the marketing macOS version.
Navigation
- Previous day: Day 5 — Text, Images, and Sound as Data.
- Next day: Day 7 — Processes, Threads, and Scheduling.
Expected output
FIELDS.md
# Required profile sections and fields (all platforms)
A correct run of `explore_os.sh` prints, in order:
1. `=== Meet Your Operating System ===`
2. `Generated on: YYYY-MM-DD`
3. `--- Kernel ---` with `Kernel line (uname -a): <full uname -a line>` beginning `Darwin` (macOS) or `Linux`
4. `--- Uptime ---` with an `Uptime:` line containing the up-duration and load averages
5. `--- Identity ---` with `Logged-in user: <username>` and `Login shell: </path/to/shell>`
6. `--- Mounted filesystems ---` with the `df -h` header and data row for `/`
7. `--- Top 5 processes by memory ---` with up to five rows: user, PID, %MEM, command (trimmed to 60 chars)
8. `--- Where the kernel lives ---` (see platform differences below)
9. `=== End of OS profile ===`
No field may read `unknown` in a completed solution.
`sample-macos.txt` in this directory is a real captured run (macOS, Apple
Silicon, 2026-07-12). A Linux capture is deliberately not included — this
lab was executed on macOS, and we do not fabricate output. On Linux, expect
these honest differences:
- **Kernel line** begins `Linux <hostname> <release> ...` and the release
looks like `6.8.0-xx-generic`; `Kernel name and release` reads
`Linux 6.8.0-...`.
- **Uptime** format differs slightly by distribution (`up 3 days, 2:14` and
similar variations).
- **Login shell** is typically `/bin/bash` rather than macOS's `/bin/zsh`.
- **Mounted filesystems**: the count is usually higher (Linux mounts many
virtual filesystems such as `tmpfs`, `proc`, `sysfs`); the root row names
a device like `/dev/sda2` or `/dev/nvme0n1p2` and shows sizes in `G`
rather than `Gi` on some distributions.
- **Top processes**: same columns; typical top entries are browsers,
desktop services, or — on servers — daemons like `mysqld`.
- **Where the kernel lives**: prints
`Linux kernel image found at: /boot/vmlinuz-<release>` with its size
(tens of MB). On WSL or inside a container there is usually no
`/boot/vmlinuz*`, and the script honestly prints
`No /boot/vmlinuz* image visible from this environment.` with an
explanation — that output is correct, not a failure.
sample-macos.txt
=== Meet Your Operating System ===
Generated on: 2026-07-12
--- Kernel ---
Kernel line (uname -a): Darwin <host> 25.5.0 Darwin Kernel Version 25.5.0: Mon Apr 27 20:41:15 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T6041 arm64
Kernel name and release: Darwin 25.5.0
--- Uptime ---
Uptime: 8:59 up 1 day, 11:39, 1 user, load averages: 2.91 2.99 2.89
--- Identity ---
Logged-in user: you
Login shell: /bin/zsh
--- Mounted filesystems ---
Mounted filesystems reported by df: 12
Root filesystem (mounted on /):
Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
/dev/disk3s1s1 926Gi 13Gi 436Gi 3% 459k 4.3G 0% /
--- Top 5 processes by memory ---
USER PID %MEM COMMAND
you 7792 2.6% /Applications/Google Chrome.app/Contents/Frameworks/Google C
you 2290 2.1% /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
you 53508 2.0% claude
you 87088 1.5% /Applications/Google Chrome.app/Contents/Frameworks/Google C
you 13660 1.5% /Applications/Microsoft Teams.app/Contents/Helpers/Microsoft
--- Where the kernel lives ---
macOS kernel (XNU) found at: /System/Library/Kernels/kernel
size: 18M owner: root
=== End of OS profile ===
Source files
examples/explore_os.sh (2508 bytes)
#!/usr/bin/env bash
# Day 006 lab — completed reference implementation.
# A guided tour of your operating system from the terminal: kernel, uptime,
# identity, shell, filesystems, top memory consumers, and where the kernel
# itself lives on disk. Supports macOS (Darwin) and Linux (including WSL);
# Windows users run this inside WSL.
#
# Note: we use `set -eu` here, deliberately without `pipefail` — pipelines
# like `ps aux | sort | head` end with head closing the pipe early, which
# is normal and expected, not an error.
set -eu
os="$(uname -s)"
echo "=== Meet Your Operating System ==="
echo "Generated on: $(date '+%Y-%m-%d')"
echo "--- Kernel ---"
echo "Kernel line (uname -a): $(uname -a)"
echo "Kernel name and release: $(uname -s) $(uname -r)"
echo "--- Uptime ---"
echo "Uptime:$(uptime)"
echo "--- Identity ---"
echo "Logged-in user: $(whoami)"
echo "Login shell: ${SHELL}"
echo "--- Mounted filesystems ---"
mount_count="$(df -h | tail -n +2 | wc -l | tr -d ' ')"
echo "Mounted filesystems reported by df: ${mount_count}"
echo "Root filesystem (mounted on /):"
df -h / | sed 's/^/ /'
echo "--- Top 5 processes by memory ---"
# Column 4 of `ps aux` is %MEM on both macOS and Linux; sort by it, descending.
echo " USER PID %MEM COMMAND"
ps aux | sort -k4 -nr | head -5 | awk '{
cmd = $11; for (i = 12; i <= NF; i++) cmd = cmd " " $i;
printf " %-12s %6s %4s%% %.60s\n", $1, $2, $4, cmd
}'
echo "--- Where the kernel lives ---"
if [ "${os}" = "Darwin" ]; then
kernel_path="/System/Library/Kernels/kernel"
if [ -f "${kernel_path}" ]; then
echo "macOS kernel (XNU) found at: ${kernel_path}"
ls -lh "${kernel_path}" | awk '{print " size: " $5 " owner: " $3}'
else
echo "macOS kernel file not found at ${kernel_path} on this system."
echo "(Some macOS versions relocate it; the kernel is still running — you are using it right now.)"
fi
elif [ "${os}" = "Linux" ]; then
kernel_image="$(ls /boot/vmlinuz* 2>/dev/null | head -n 1 || true)"
if [ -n "${kernel_image}" ]; then
echo "Linux kernel image found at: ${kernel_image}"
ls -lh "${kernel_image}" | awk '{print " size: " $5 " owner: " $3}'
else
echo "No /boot/vmlinuz* image visible from this environment."
echo "(Common and honest on WSL and in containers: the running kernel is supplied from outside this filesystem.)"
fi
else
echo "Unsupported OS for this script: ${os} (Windows users: run this inside WSL)" >&2
exit 1
fi
echo "=== End of OS profile ==="
metadata.yml (563 bytes)
lesson_id: D006
day: 6
kind: os-exploration
languages: [bash]
setup_commands:
- cd labs/sections/computing-foundations/day-006-operating-systems-what-they-do-and
run_commands:
- bash examples/explore_os.sh
- bash starter/explore_os.sh
test_commands:
- bash tests/run_tests.sh
cleanup_commands:
- 'git checkout -- starter/explore_os.sh # optional: reset your work'
requires_network: false
requires_api_key: false
estimated_minutes: 30
last_executed: '2026-07-12'
executed_on: 'macOS (Apple Silicon), bash tests/run_tests.sh → 29 checks, 0 failure(s)'
requirements/README.md (750 bytes)
# Dependencies — Day 006 lab
**None beyond a POSIX shell.** This lab intentionally has zero installable
dependencies:
- `bash` ≥ 3.2 (preinstalled on macOS and every mainstream Linux distribution)
- Standard OS utilities only: `uname`, `date`, `uptime`, `whoami`, `df`,
`ps`, `sort`, `head`, `awk`, `sed`, `ls`, `wc` — all part of the base
system on macOS and Linux (and inside WSL).
Windows users need WSL (`wsl --install`) — a one-time setup that the rest
of this course relies on from Week 2 onward, so today is the right day to
do it.
There is deliberately no `requirements.txt`/`package.json` here; like Day 1,
this lab must run on a factory-fresh machine. Later labs declare their
Python or Node dependencies in this directory.
starter/explore_os.sh (3298 bytes)
#!/usr/bin/env bash
# Day 006 lab — meet your operating system from the command line.
#
# This starter script already prints the report skeleton and handles the
# OS-specific "where is the kernel" section for you. Your job is to complete
# the five numbered exercises below, replacing each `unknown` assignment with
# the command named in the comment, in `$(...)` form. The completed reference
# version is in examples/explore_os.sh — try it yourself first.
#
# Note: we use `set -eu` here, deliberately without `pipefail` — pipelines
# like `ps aux | sort | head` end with head closing the pipe early, which
# is normal and expected, not an error.
set -eu
os="$(uname -s)"
if [ "${os}" != "Darwin" ] && [ "${os}" != "Linux" ]; then
echo "Unsupported OS for this script: ${os} (Windows users: run this inside WSL)" >&2
exit 1
fi
echo "=== Meet Your Operating System ==="
echo "Generated on: $(date '+%Y-%m-%d')"
echo "--- Kernel ---"
# Exercise 1: set kernel_line using: uname -a
# (uname asks the kernel to describe itself: name, release, architecture.)
kernel_line="unknown"
echo "Kernel line (uname -a): ${kernel_line}"
echo "--- Uptime ---"
# Exercise 2: set up_line using: uptime
# (how long the kernel has been running since boot, plus load averages.)
up_line="unknown"
echo "Uptime: ${up_line}"
echo "--- Identity ---"
# Exercise 3a: set current_user using: whoami
current_user="unknown"
# Exercise 3b: set login_shell from the SHELL environment variable: echo "$SHELL"
# (hint: you do not even need a command — "${SHELL}" is already the value.)
login_shell="unknown"
echo "Logged-in user: ${current_user}"
echo "Login shell: ${login_shell}"
echo "--- Mounted filesystems ---"
# Exercise 4: set root_fs to the df -h report for the root filesystem: df -h /
# (both header and data row; keep the quotes so the newline survives.)
root_fs="unknown"
echo "Root filesystem (mounted on /):"
echo "${root_fs}" | sed 's/^/ /'
echo "--- Top 5 processes by memory ---"
# Exercise 5: set top_procs using: ps aux | sort -k4 -nr | head -5
# (ps aux lists every process; column 4 is %MEM; sort -k4 -nr orders by it,
# biggest first; head -5 keeps the top five.)
top_procs="unknown"
echo "${top_procs}" | awk '{
cmd = $11; for (i = 12; i <= NF; i++) cmd = cmd " " $i;
printf " %-12s %6s %4s%% %.60s\n", $1, $2, $4, cmd
}'
echo "--- Where the kernel lives ---"
if [ "${os}" = "Darwin" ]; then
kernel_path="/System/Library/Kernels/kernel"
if [ -f "${kernel_path}" ]; then
echo "macOS kernel (XNU) found at: ${kernel_path}"
ls -lh "${kernel_path}" | awk '{print " size: " $5 " owner: " $3}'
else
echo "macOS kernel file not found at ${kernel_path} on this system."
echo "(Some macOS versions relocate it; the kernel is still running — you are using it right now.)"
fi
else
kernel_image="$(ls /boot/vmlinuz* 2>/dev/null | head -n 1 || true)"
if [ -n "${kernel_image}" ]; then
echo "Linux kernel image found at: ${kernel_image}"
ls -lh "${kernel_image}" | awk '{print " size: " $5 " owner: " $3}'
else
echo "No /boot/vmlinuz* image visible from this environment."
echo "(Common and honest on WSL and in containers: the running kernel is supplied from outside this filesystem.)"
fi
fi
echo "=== End of OS profile ==="
starter/os-profile-worksheet.md (1892 bytes)
# My OS Profile — Day 006 worksheet
This worksheet extends the machine profile you built on Day 1 with the
operating-system layer. Fill in every value from your own machine using the
lab script or the individual commands from the lesson's hands-on section.
Keep it next to your Day 1 worksheet — the Week 1 project uses both.
| Field | Your value | Command you used |
| ---------------------------------- | ---------- | ---------------- |
| Date measured | | |
| Kernel name | | |
| Kernel release/version | | |
| OS / distribution name and version | | |
| Uptime at time of measurement | | |
| Logged-in user | | |
| Login shell | | |
| Filesystems mounted (count) | | |
| Root filesystem free space | | |
| Total process count | | |
| Top memory-consuming process | | |
## One process I looked up
Pick one process from `ps aux` whose name you did not recognize. Search the
web for its name plus your OS, then write two or three sentences: what it
is, and which of the OS's five jobs (processes, memory, filesystems,
drivers, security) it serves.
## My machine's two worlds
Write one paragraph: name three programs from your process list that you
started yourself and three you never started, and explain — in kernel/user-
space terms — why all six still had to go through the same system-call
boundary for everything they have done since boot.
## One thing that surprised me
One or two sentences.
tests/run_tests.sh (3264 bytes)
#!/usr/bin/env bash
# Tests for the Day 006 lab. Run from the lab directory:
# bash tests/run_tests.sh
#
# Verifies that the completed reference script exits cleanly and prints every
# required section of the OS profile with real (non-"unknown") values, and —
# once the learner has finished the starter script — checks their version the
# same way. Exits 0 on success, 1 on any failure.
set -u
lab_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
failures=0
checks=0
check() {
local label="$1" ok="$2"
checks=$((checks + 1))
if [ "${ok}" = "yes" ]; then
echo " ok: ${label}"
else
echo " FAIL: ${label}"
failures=$((failures + 1))
fi
}
run_profile_checks() {
local script="$1" strict="$2" output
echo "Testing ${script} ..."
if ! output="$(bash "${script}" 2>&1)"; then
check "script exits successfully" "no"
echo "${output}" | sed 's/^/ /'
return
fi
check "script exits successfully" "yes"
echo "${output}" | grep -q '^=== Meet Your Operating System ===$' && check "prints profile header" "yes" || check "prints profile header" "no"
echo "${output}" | grep -q '^=== End of OS profile ===$' && check "prints profile footer" "yes" || check "prints profile footer" "no"
for section in "--- Kernel ---" "--- Uptime ---" "--- Identity ---" "--- Mounted filesystems ---" "--- Top 5 processes by memory ---" "--- Where the kernel lives ---"; do
echo "${output}" | grep -q "^${section}$" && check "prints section '${section}'" "yes" || check "prints section '${section}'" "no"
done
for field in "Kernel line (uname -a):" "Logged-in user:" "Login shell:" "Root filesystem (mounted on /):"; do
echo "${output}" | grep -q "^${field}" && check "prints '${field}'" "yes" || check "prints '${field}'" "no"
done
if [ "${strict}" = "strict" ]; then
if echo "${output}" | grep -q "unknown"; then
check "no field is left 'unknown'" "no"
else
check "no field is left 'unknown'" "yes"
fi
# The kernel line must actually name a kernel this course supports.
if echo "${output}" | grep -E '^Kernel line \(uname -a\): (Darwin|Linux)' >/dev/null; then
check "kernel line names Darwin or Linux" "yes"
else
check "kernel line names Darwin or Linux" "no"
fi
# The top-processes section must contain at least one PID-looking row.
procs_section="$(echo "${output}" | sed -n '/^--- Top 5 processes by memory ---$/,/^--- Where the kernel lives ---$/p')"
if echo "${procs_section}" | grep -E '[0-9]+ ' >/dev/null; then
check "top-processes section lists real processes" "yes"
else
check "top-processes section lists real processes" "no"
fi
fi
}
run_profile_checks "${lab_dir}/examples/explore_os.sh" strict
# The starter ships with 'unknown' values on purpose; once the learner has
# replaced them all, hold their script to the same strict standard.
if grep -q '"unknown"' "${lab_dir}/starter/explore_os.sh"; then
echo "Note: starter/explore_os.sh still has unfilled exercises — testing structure only."
run_profile_checks "${lab_dir}/starter/explore_os.sh" lenient
else
run_profile_checks "${lab_dir}/starter/explore_os.sh" strict
fi
echo
echo "${checks} checks, ${failures} failure(s)."
[ "${failures}" -eq 0 ]
Troubleshooting
Troubleshooting — Day 006 lab
ps: illegal option or strange ps output
You are probably in PowerShell or Command Prompt on Windows — those are not
Unix shells. Open a WSL terminal (wsl from PowerShell after
wsl --install) and run the lab there. ps aux works on macOS, Linux, and
WSL.
Login shell: prints nothing (or the tests flag it)
Use the exact form login_shell="${SHELL}" in the script (or
echo "$SHELL" at the prompt). The dollar sign asks the shell to expand
the variable; without quotes and the $, you print a literal word or
nothing. In rare minimal environments SHELL may be unset — run
echo $0 to see what shell you are actually in.
The script dies partway with no message
If you experimented with set -euo pipefail, note the deliberate comment
at the top of both scripts: ps aux | sort | head ends with head closing
the pipe early, which pipefail treats as a failure. The shipped scripts
use set -eu for exactly this reason — restore that line.
df -h shows a forest of rows and the mount count looks huge
Normal. macOS splits the system across several APFS volumes, and Linux
mounts many virtual filesystems (tmpfs, proc, sysfs, snap loops).
For this lab, only the row whose last column is / matters.
No /boot/vmlinuz* image visible from this environment. on Linux
On WSL and inside containers this is the correct, honest answer: the
running kernel is supplied from outside the filesystem you can see (by
Windows in WSL's case). The tests accept this output. On a regular Linux
install, if /boot is empty you may need sudo ls /boot to check whether
it is a permissions issue — but do not sudo the lab script itself; it
never needs it.
The top-memory list shows the same app five times
Also normal, and worth noticing: browsers and chat apps run each tab or view as a separate isolated process on purpose. The lesson's security section explains why that isolation is a feature.
%MEM values don't add up to 100%
They never will: shared memory is counted in several processes at once,
and the kernel's own memory belongs to no process row. Treat %MEM as a
ranking, not an audit.
Tests fail with no field is left 'unknown'
That check is telling you an exercise is still unfinished — search the
starter for "unknown" and complete the remaining assignments.
Windows: bash is not recognized
Install WSL: open PowerShell as administrator, run wsl --install,
reboot, open the Ubuntu app, and run the lab from there. From Week 2
onward the course assumes a Unix-style shell, so this setup pays off
immediately.
Security notes
Security notes — Day 006 lab
- What the scripts do: read information the OS already publishes
(
uname,uptime,whoami,df,ps, and a read-onlylsof the kernel file) and print it. They make no network connections, write no files, change no settings, and are strictly read-only. - Privileges: everything runs as your normal user. Nothing in this lab
needs
sudo— and today's lesson explains exactly why that matters: the kernel/user-space boundary is the machine's security foundation, andsudoasks the OS to suspend it. If any instruction ever tells you tosudoa script you haven't read, stop and read it first. - Privacy —
pssees a lot: on a shared or multi-user machine,ps auxlists other users' processes too, including their full command lines. Command lines sometimes contain sensitive material (file names, server addresses, and — a classic mistake — passwords passed as arguments). Two habits follow: never put secrets on a command line, and treat process listings from shared machines as confidential. - Sharing your output: an OS profile (kernel version, uptime, process names) reveals your patch level and the software you run — useful to an attacker profiling a target. Sharing it in a class forum is normally fine; avoid posting profiles of employer-managed machines, and skim the process list for anything personal before pasting.
- Reading before running: both scripts are short and commented — read them first. Running unread shell scripts is one of the most common ways developers get compromised; every lab script in this course is small enough to read and understand before executing.