sysvinit and systemd, shutting the operating system down starts by notifying the
init process (the process with pid 1) that the system should shut down (or reboot, or power off).
sysvinit does this by using the
/run/initctl FIFO to communicate with
init, asking it to switch to the corresponding runlevel. See the
init manpage for a brief overview.
systemd supports a variety of ways to do this. Various signals can be used to request a shutdown, reboot, etc., in various flavours; it’s also possible to request this over d-bus (the
busctl manpage explains how to explore that).
Once pid 1 has been asked to shut down, it follows its configuration and specification and goes through all the appropriate steps for it. This typically includes notifying all users that a shutdown is in progress, shutting down all the running services (in a managed way with systemd; using shutdown scripts in various forms with
sysvinit), syncing the mounted file systems, possibly unmounting them, killing all remaining processes (with the usual
KILL sequence), and finally calling the kernel’s
reboot system call with the appropriate parameters.
This describes the general sequence. There are many more twists to all this, including access control (with Polkit), various available hooks,
kexec, sudden power-off handling, CtrlAltDel handling... The systemd documentation covers many of these details.