Computing FoundationsInside the Machine › Day 6

Hands-on lab — Day 6: Operating Systems: What They Do and Why

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

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-ComputerInfo and 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

  1. bash starter/explore_os.sh exits with no error.
  2. Every section header prints (Kernel, Uptime, Identity, Shell, Filesystems, Top processes).
  3. Your kernel name matches your OS (Darwin on macOS, Linux on Linux/WSL).
  4. 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.mdps 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

  1. Add the number of running processes (ps -ax | wc -l) to your worksheet and compare it with your CPU core count from Day 1.
  2. Look up one unfamiliar process from the top-memory list and record what it is.
  3. On Linux, print your distribution's PRETTY_NAME from /etc/os-release; on macOS, print the Darwin version and map it to the marketing macOS version.
  • 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-only ls of 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, and sudo asks the OS to suspend it. If any instruction ever tells you to sudo a script you haven't read, stop and read it first.
  • Privacy — ps sees a lot: on a shared or multi-user machine, ps aux lists 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.