How SysVinit Managed the Linux Startup Sequence

In older Linux distributions, System V Init (SysVinit) served as the standard init system responsible for bootstrapping user space after the Linux kernel loaded. It orchestrated the startup sequence using a deterministic, sequential mechanism centered around the first user-space process (init with PID 1), system runlevels, configuration directives defined in /etc/inittab, and a structured series of numbered shell scripts. This architecture ensured services launched in a precise order, transitioning the operating system from a bare kernel state to a fully operational, multi-user environment.

The Kernel Handoff and PID 1

The boot sequence began once the Linux kernel finished initializing hardware drivers and mounting the root filesystem. The kernel then executed the /sbin/init binary as Process ID 1 (PID 1). As the parent of all subsequent processes, init immediately read its primary configuration file, /etc/inittab, to determine the default target state, known as the default runlevel, and execute early system preparation commands.

The Concept of Runlevels

SysVinit organized operating states into standard tiers called runlevels. Each runlevel represented a distinct system mode with a predefined set of running services:

The /etc/inittab file declared the default runlevel through the initdefault entry (for example, id:3:initdefault:).

The Inittab and the /etc/rc Script

Upon reading the target runlevel, init ran initial system setup scripts—often /etc/rc.sysinit—to set the system clock, enable swap space, check and mount remaining filesystems, and configure hostnames.

Once system initialization completed, init invoked the main runlevel controller script (typically /etc/init.d/rc or /etc/rc) passing the target runlevel number as an argument.

Script Layout and the Naming Convention

SysVinit managed system services through modular shell scripts kept in a central directory, usually /etc/init.d/. These scripts accepted standard arguments such as start, stop, restart, and status.

To manage which services ran at each runlevel, SysVinit used directories corresponding to each level, such as /etc/rc0.d/ through /etc/rc6.d/. These directories did not contain actual executables; instead, they contained symbolic links pointing back to the master scripts in /etc/init.d/.

The symbolic links followed a strict naming convention:

Sequential Execution

When transitioning into a new runlevel, the /etc/rc program executed the directory's symlinks in strict alphanumeric sequence:

  1. Kill Scripts First: The system executed all scripts starting with K in ascending numerical order, invoking them with the stop argument. This gracefully terminated services not intended to run in the target runlevel.
  2. Start Scripts Second: The system executed all scripts starting with S in ascending numerical order, invoking them with the start argument. Low-numbered services (e.g., S10network) started before high-numbered dependencies (e.g., S80apache2), ensuring prerequisites were met prior to application initialization.

Because SysVinit ran these scripts serially in a single-threaded queue, any stalled or slow script directly delayed the entire boot process.

Finalizing the Boot

Once all runlevel scripts completed, init returned to /etc/inittab to launch lingering processes set to respawn automatically, such as getty processes on virtual terminals (ttys) or graphical display managers (like GDM or LightDM in runlevel 5). With the login prompts active, the SysVinit startup sequence was complete.