Processes & jobs

The process model — a PID table with status tracking, job control, ps/top/kill, and a systemd-lite service manager.

Lifo models processes like Linux does: a table of entries with PIDs, parents, status, and a way to signal them. The ProcessRegistry (on kernel.processRegistry) owns it.

The process table

interface Process {
  pid: number;
  ppid: number;                 // parent (shell is PID 1)
  command: string;
  args: string[];
  status: "running" | "sleeping" | "stopped" | "zombie";
  controller: AbortController;  // how it's killed
  // …start time, etc.
}
  • PID 1 is the shell; spawned processes get PIDs from 2 up.
  • SpawningprocessRegistry.spawn(opts) registers a process and returns its PID. When its promise settles it auto-transitions to zombie (awaiting reap), mirroring Unix.
  • Killing — each process carries an AbortController; killing it aborts the signal the command is watching (ctx.signal), so well-behaved commands stop promptly.

Inspecting and controlling

The usual tools are built in and operate on the registry:

  • ps — list processes (PID, PPID, status, command).
  • top — a live process view.
  • kill — signal / abort a process by PID.
  • watch — re-run a command on an interval.
await sandbox.commands.run("ps");
await sandbox.commands.run("kill 7");

Job control

Foreground and background jobs are tracked by the shell's JobTable. A trailing & backgrounds a pipeline; jobs, fg, and bg manage them, and Ctrl-Z / stop transitions map onto the stopped status.

Services: a systemd-lite

For long-running processes (dev servers, daemons), the kernel includes a ServiceManager driven by unit files and exposed through systemctl:

await sandbox.commands.run("systemctl start my-service");
await sandbox.commands.run("systemctl status my-service");

Unit files are parsed by parseUnitFile (UnitFile), and the manager tracks each service's ServiceInfo (state, PID, restarts). This is how a box can bring up background services declaratively rather than juggling raw & jobs.

Note

The process model is cooperative: "killing" aborts an AbortSignal rather than tearing down an OS process. Commands that honor ctx.signal (all the built-ins do) stop cleanly; the Node runtime wires the same signal into a script's process and its async work.