What is an operating system?
The invisible manager
Every time you run bin/rails server, something manages the thousands of details required to make it work: allocating RAM for Ruby objects, scheduling CPU time between your server and your Spotify playlist, reading files from disk, and routing network packets from the browser to Puma.
You never asked anything to do this. The operating system (OS) did it all, silently, behind the scenes.
An operating system is the software layer between your applications and the hardware. It is the manager that ensures every program gets the resources it needs without stepping on other programs.
flowchart TB
APP["Your applications<br/>Ruby · Chrome · VS Code · PostgreSQL"]
OS["Operating system<br/>process manager · memory manager · file system<br/>device drivers · network stack · security"]
HW["Hardware<br/>CPU · RAM · SSD · network card"]
APP --> OS --> HW
The five jobs of an OS
Every operating system (macOS, Linux, Windows) performs the same five core jobs:
1. Process management
The OS decides which programs run, when they run, and how much CPU time each gets. Your computer might have 200+ processes running simultaneously, but only 8 CPU cores: the OS juggles them.
2. Memory management
The OS allocates RAM to each process, protects processes from accessing each other's memory, and handles what happens when RAM fills up (swapping to disk).
3. File system management
The OS organizes data on disk into files and directories, handles read/write permissions, and manages concurrent access (two programs writing to the same file).
4. Device management
The OS communicates with hardware through device drivers: keyboard, mouse, display, network card, SSD. Applications never talk to hardware directly.
5. Security and access control
The OS enforces who can read, write, or execute files. This is why chmod, file permissions, and user accounts exist.
The kernel: the heart of the OS
The kernel is the core of the operating system. It is the only software that runs with full hardware access, called kernel mode (ring 0 on x86 CPUs).
Everything else (Ruby, Chrome, PostgreSQL) runs in user mode (ring 3) with restricted access. When a user-mode program needs hardware access (read a file, send a network packet), it makes a system call to the kernel.
flowchart TB
U["User mode (ring 3)<br/>Ruby, Chrome, Puma, PostgreSQL<br/>restricted access"]
S["System calls (the gate)<br/>open(), read(), write(), fork(), exec(), socket()"]
K["Kernel mode (ring 0)<br/>scheduler, memory manager, file system, drivers<br/>full hardware access"]
H["Hardware"]
U --> S --> K --> H
System calls are the only way user programs interact with hardware. When Ruby executes File.read("data.csv"), it triggers the open() and read() system calls, which ask the kernel to access the disk on Ruby's behalf.
macOS, Linux, and Windows
As a Rails developer, you will encounter all three:
| OS | Kernel | Where You Use It |
|---|---|---|
| macOS | XNU (Mach + BSD) | Your development machine (MacBook) |
| Linux | Linux | Production servers (Ubuntu, Debian, Alpine) |
| Windows | NT | WSL2 for Rails development |
macOS and Linux are both Unix-like: they share the same concepts (processes, file permissions, pipes, signals) and most terminal commands work identically. This is why Rails development on macOS transfers seamlessly to Linux production servers.
Windows is fundamentally different internally, which is why Rails on Windows uses WSL2 (Windows Subsystem for Linux): it runs a real Linux kernel inside Windows.
Unix philosophy
Unix (the ancestor of macOS and Linux) was built on a philosophy you'll recognize in well-organized Rails code:
"Do one thing and do it well."
- Small, focused programs that can be combined
- Text as the universal interface
- Everything is a file
You see this in how Unix commands work:
# Each command does ONE thing — then they are combined with pipes
cat server.log | grep "ERROR" | wc -l
# read file filter lines count them
The same idea shows up in Rails code:
- Single Responsibility: Each class does one thing
- Small service and query objects: One job each, combined by the caller
- DDD boundaries: Each domain handles its own responsibility
What happens when you turn on your computer
The boot process reveals how the OS takes control:
- BIOS/UEFI: Firmware on the motherboard runs first, checks hardware
- Bootloader (GRUB, systemd-boot): Loads the kernel from disk into RAM
- Kernel initialization: The kernel sets up memory management, device drivers, and the process scheduler
- Init system (systemd, launchd): The first user-mode process starts: PID 1
- Services start: PostgreSQL, Nginx, SSH, and other daemons launch
- Login: The graphical interface or terminal appears
flowchart LR
A["Power on"] --> B["BIOS/UEFI"] --> C["Bootloader"] --> D["Kernel"] --> E["Init (PID 1)"] --> F["Services"] --> G["Login"]
The init system (PID 1) is the parent of all other processes. On Linux, it is usually systemd. On macOS, it is launchd. On a classic server setup, systemd keeps Puma running and restarts it if it crashes. This app deploys with Kamal, so Docker restarts the app's container instead.
Seeing the OS in action
You can observe the OS working right now:
# How many processes are running?
ps aux | wc -l
# => 287 (typical for a development machine)
# How much RAM is in use?
free -h # Linux
vm_stat # macOS
# What is the CPU doing?
top # Real-time process monitor
htop # Better version (install with brew/apt)
# What files does Ruby have open?
lsof -p $(pgrep -f "ruby") # List open files for Ruby process
From Ruby, you can query the OS directly:
# Current process ID
Process.pid # => 42731
# Parent process ID
Process.ppid # => 42700
# Number of CPU cores
require 'etc'
Etc.nprocessors # => 8
# Current working directory
Dir.pwd # => "/Users/dev/myapp"
# Environment variables (set by the OS shell)
ENV['HOME'] # => "/Users/dev"
ENV['PATH'] # => "/usr/local/bin:/usr/bin:..."
ENV['RAILS_ENV'] # => nil unless set (Rails.env then defaults to "development")
Why Rails developers need to understand the OS
| Situation | OS Knowledge Required |
|---|---|
| Deploy to production | Linux process management, systemd, file permissions |
| Debug memory leaks | OS memory management, ps, top, swap |
| Configure Puma workers | Process forking, CPU cores, memory per process |
| Set up PostgreSQL | File I/O, shared memory (shared_buffers), Unix sockets |
| Handle file uploads | File system, temp directories, permissions |
| Use environment variables | Shell, process inheritance, .env files |
| Debug "too many open files" | File descriptors, ulimit, OS limits |
The OS is not an abstraction you can ignore. Every Rails app runs on an OS, and production bugs often live in the OS layer.
Summary
| Concept | What It Means |
|---|---|
| Operating System | Software layer between applications and hardware |
| Kernel | Core of the OS with full hardware access (ring 0) |
| System calls | The only way user programs access hardware (open, read, write, fork) |
| User mode vs Kernel mode | Applications are restricted; only the kernel touches hardware |
| Unix-like | macOS and Linux share the same concepts and commands |
| Init system (PID 1) | First process; manages all other services (systemd, launchd) |
| Unix philosophy | "Do one thing well": also good Rails design |
Keep going with lesson 2
Processes: programs in action
Explains processes, PIDs and states, fork and exec, copy-on-write in Puma, zombies, signals and environment variables.
A free account also gives you the quizzes, your progress and notes, and the discussion.