Overview
This article examines the boot sequence of the Linux operating system, focusing on Debian-based systems.
Context: BIOS vs Operating System
During the BIOS stage the computer's behavior is largely fixed and offers limited options for programmers. Once the operating system takes over, most aspects become configurable, so the boot process is closely related to software behavior.
1. Loading the Kernel
After the firmware hands control to the operating system, the kernel image in the /boot directory is read into memory.
Example contents of /boot on the author’s machine:
$ ls /boot
config-3.2.0-3-amd64
config-3.2.0-4-amd64
grub
initrd.img-3.2.0-3-amd64
initrd.img-3.2.0-4-amd64
System.map-3.2.0-3-amd64
System.map-3.2.0-4-amd64
vmlinuz-3.2.0-3-amd64
vmlinuz-3.2.0-4-amd64
2. Starting the init Process
Once the kernel is loaded, it starts the first userland process: /sbin/init. This process initializes the system environment.
init is the first user process and therefore has process ID 1. Other processes are spawned from it.
3. Determining the Runlevel
Many programs need to start at boot. On Windows these are called "services"; on Linux they are typically daemons. A main task of init is to run these boot-time daemons.
Different use cases require different sets of services. Linux uses runlevels to specify which services should start for a given scenario. During boot, init reads the runlevel setting and starts the corresponding services.
Linux historically defines seven runlevels (0-6). Common conventions are: 0 = halt, 1 = single-user (maintenance) mode, 6 = reboot. Runlevels 2–5 vary by distribution; on Debian they are typically equivalent multiuser modes.
init reads /etc/inittab to determine the default runlevel. For example:
id:2:initdefault:
This sets the default runlevel to 2. Each runlevel has a corresponding directory under /etc that lists the services to start:
/etc/rc0.d
/etc/rc1.d
/etc/rc2.d
/etc/rc3.d
/etc/rc4.d
/etc/rc5.d
/etc/rc6.d
Files in /etc/rcN.d typically follow the naming convention S##name or K##name. S indicates start and K indicates kill (stop). The two digits indicate order: lower numbers run earlier. If numbers match, alphabetical order of the service name is used.
$ ls ?/etc/rc2.d
README
S01motd
S13rpcbind
S14nfs-common
S16binfmt-support
S16rsyslog
S16sudo
S17apache2
S18acpid
...
4. Loading Boot Services
To avoid duplicating startup scripts across multiple runlevel directories, the entries in /etc/rcN.d are symbolic links pointing to the actual scripts in /etc/init.d. init executes those scripts to start or stop services.
$ ls -l /etc/rc2.d
README
S01motd -> ../init.d/motd
S13rpcbind -> ../init.d/rpcbind
S14nfs-common -> ../init.d/nfs-common
S16binfmt-support -> ../init.d/binfmt-support
S16rsyslog -> ../init.d/rsyslog
S16sudo -> ../init.d/sudo
S17apache2 -> ../init.d/apache2
S18acpid -> ../init.d/acpid
...
Because the real scripts live in /etc/init.d, administrators can run them directly. For example, to restart Apache:
$ sudo /etc/init.d/apache2 restart
5. User Login
After boot services are loaded, the system prompts for user login. Common login methods include:
- local console (command-line) login
- ssh login
- graphical (display manager) login
Authentication is handled differently depending on the method:
Local console: init invokes getty to prompt for username and password. After credentials are entered, login verifies the password (Debian typically runs PAM modules such as /etc/pam.d/login). On success, the user’s configured shell is read from /etc/passwd and started.
SSH: The sshd daemon handles authentication (Debian typically uses /etc/pam.d/ssh) and then starts the user’s shell.
Graphical: A display manager handles authentication (for GNOME, gdm). On success, the session script such as /etc/gdm3/Xsession is executed to start the user session.
6. Entering the Login Shell
A login shell is the shell started after authentication that reads a set of initialization files. Debian’s default shell is Bash, which reads a sequence of configuration files depending on how the session was started.
For console and SSH logins, Bash reads /etc/profile followed by one of these user files, in order:
~/.bash_profile
~/.bash_login
~/.profile
Only the first existing file in that list is read.
For graphical logins, Bash typically reads /etc/profile and ~/.profile only; ~/.bash_profile is not used in that case.
7. Opening Non-login Shells
A non-login shell is often started when a user opens an additional terminal window. Non-login shells do not read /etc/profile or ~/.profile; instead, they read ~/.bashrc. Because most user customizations are placed in ~/.bashrc, Debian’s default configuration sources ~/.bashrc from ~/.profile so that settings apply consistently.
Example in ~/.profile:
if [ -n "$BASH_VERSION" ]; then
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
If you rely on ~/.bash_profile and it prevents ~/.profile from being read, add the following to ~/.bash_profile to ensure ~/.profile (and thus ~/.bashrc) is sourced:
if [ -f ~/.profile ]; then
. ~/.profile
fi
The historical split between these files reflects performance and inheritance considerations from earlier systems: /etc/profile for system-wide settings, ~/.profile for user settings inherited by child processes, and ~/.bashrc for interactive, non-inherited settings.
Note: macOS also uses Bash as the default shell in older releases. On macOS, the typical behavior is to load ~/.bash_profile and source ~/.bashrc from there for both SSH and GUI-launched shells.