Sirui Chen·sirui.dev

Project

PennOS — A Unix-Like Educational OS

Userland shell, priority scheduler, signals, redirection, and a PennFAT filesystem in C — with a live in-browser terminal.

CPOSIX threads (spthread)Operating SystemsShellsSchedulingSignalsFilesystemsWebSocketsnode-ptyNext.jsxterm.jsprlimit / systemd sandboxing
PennOS live terminal preview

Overview

C implementation of Unix-like OS features: a shell with job control and I/O redirection, a SIGALRM-driven priority scheduler over spthread, full process lifecycle with signals and zombies, and the PennFAT on-disk filesystem — all wired up to a sandboxed live terminal on this site.

Problem

Course-scale operating systems are unforgiving — invalid input, partial writes, signal races, and bad scheduling decisions corrupt processes and on-disk state. The challenge was building OS components that behave predictably under all of that and that real visitors could actually drive in the browser without putting the host at risk.

What I built

Implemented the OS in layered C: shell parser and built-ins on top, s_* user-facing syscalls in the middle, scheduler/process and PennFAT k_* primitives at the bottom, and spthread (POSIX threads + SIGALRM) at the base. Then put the binary behind a typed Node WebSocket sidecar with per-session prlimit sandboxing so anyone can boot a real PennOS in their browser.

Architecture

  • Shell layer — parser, built-ins (ls, cat, ps, jobs, fg, bg, kill, chmod, nice, sleep), I/O redirection (< > >>), and async background jobs
  • User API (s_*) — s_spawn, s_waitpid, s_kill, s_open, s_read, s_write, s_lseek as the syscall boundary between shell and kernel
  • Process / scheduler — PCB table, three priority levels with a 9:6:4 schedule ratio, SIGALRM-driven preemption, zombie reaping, orphan reparenting to init
  • PennFAT (k_*) — FAT-style on-disk format with explicit superblock, FAT table, and directory entries, plus an fd table that maps shell fds onto kernel objects
  • spthread substrate — POSIX threads with cooperative-style suspend/resume primitives used to implement context switches and signal delivery
  • Live terminal — Node + ws + node-pty sidecar at 127.0.0.1:3100, prlimit-wrapped (CPU 120s, RSS 96 MiB, FSIZE 2 MiB, NOFILE 32, NPROC 24), per-session ephemeral PennFAT image, idle/hard timeouts, reverse-proxied as /ws/pennos under Nginx with a per-IP rate-limit zone

Technical highlights

  • Three-level priority scheduler with 9:6:4 quantum ratio, preempted by SIGALRM, riding on spthread suspend/resume primitives.
  • Terminal job control: jobs, fg %N, bg %N, Ctrl-C / Ctrl-Z forwarding, orphan reparenting to init, zombie reaping via s_waitpid.
  • I/O redirection (<, >, >>) plumbed through the s_* fd table that wraps PennFAT k_* file operations.
  • Stress demos exposed as shell commands: busy, hang, nohang, recur, crash, zombify, orphanify — built specifically to break the kernel and prove it doesn't.
  • PennFAT on-disk format: explicit superblock, FAT-style chain table, single root directory, file permission bits (r/w/x) settable via chmod +/-rwx.
  • Live in-browser session uses a typed JSON wire protocol over WS, per-IP and global session caps, prlimit-wrapped child, and tmpdir-per-session cleanup on disconnect.

Impact

  • Built a small but realistic OS kernel surface area exercising scheduling, signals, and filesystem code under hostile inputs.
  • Shipped the project as a hands-on artifact rather than a tarball: visitors can boot a sandboxed PennOS in the browser without me running anything for them.