An introduction to the Linux boot and startup processes with GPT and GRUB2

0

Last Updated on April 14, 2024 by Jim Hall

Image by: Penguin, Boot. Modified by Opensource.com. CC BY-SA 4.0.

Author’s note: This article is an almost complete rewrite of a previous article that looked at Linux boot and startup from a legacy MBR standpoint. This article explores boot and startup from storage devices formatted using the GPT.

Have you ever wondered what it takes to get your system ready to run applications? Here’s what is going on under the hood.

Understanding the Linux boot and startup processes is important to being able to both configure Linux and to resolving startup issues. This article presents an overview of the bootup sequence using the GRUB2 bootloader and the startup sequence as performed by the systemd initialization system. This article also touches upon disk partitioning.1

In reality, there are two sequences of events that are required to boot a Linux computer and make it usable: boot and startup. The boot sequence starts when the computer is turned on, and is completed when the kernel is initialized and systemd is launched. The startup process then takes over and finishes the task of getting the Linux computer into an operational state.

Overall, the Linux boot and startup process is fairly simple to understand. It is comprised of the following steps which will be described in more detail in the following sections.

  1. BIOS/UEFI POST
  2. Bootloader (GRUB2)
  3. Kernel
  4. systemd

It is important to separate the hardware boot from the Linux boot process from the Linux startup and to explicitly define the demarcation points between them. Understanding these differences and what part each plays in getting a Linux system to a state where it can be productive makes it possible to manage these processes and to better determine the portion in which a problem is occurring during what most people refer to as “boot.”

Note that this article covers GRUB2 and systemd because they are the current boot loader and initialization software for most major distributions. Other software options have been used historically and are still found in some distributions.

The boot process

The boot process can be initiated in one of a couple ways. First, if power is turned off, turning on the power will begin the boot process. If the computer is already running a local user, including root or an unprivileged user, the user can programmatically initiate the boot sequence by using the GUI or command line, or pressing the Reset button, if there is one, to initiate a reboot. A reboot will first do a shutdown and then restart the computer.

BIOS/UEFI POST

The first step of the Linux boot process really has nothing whatever to do with Linux. This is the hardware portion of the boot process and is the same for any operating system. When power is first applied to the computer it runs the POST (Power On Self Test) which is part of the old BIOS (Basic I/O System) or its new replacement, the Unified Extended Firmware Interface (UEFI) .

When IBM designed the first PC back in 1981, BIOS was designed to identify, test, and initialize the installed hardware components. POST is the part of BIOS/UEFI whose task is to ensure that the computer hardware functioned correctly. If POST fails, the computer may not be usable and so the boot process does not continue.

You can determine the type of firmware installed on your system using the command line program in Figure 1. This shows that my computer uses UEFI firmware.

root@david:~# [ -d /sys/firmware/efi ] && echo UEFI || echo BIOS
UEFI

Figure 1: This command line program displays UEFI if your computer uses the Extended Firmware Interface (EFI), or BIOS when using legacy BIOS.

POST checks the basic operability of the hardware and then it issues a BIOS/UEFI interrupt, INT 13H, a part of the firmware that locates the boot sectors on any attached bootable devices. The first boot sector it finds that contains a valid boot record is loaded into RAM and control is then transferred to the code that was loaded from the Master Boot Record (MBR).

GRUB

The primary function of the GRUB bootloader is to get the Linux kernel loaded into memory and running.

GRUB2 stands for “GRand Unified Bootloader, version 2” and it is now the primary bootloader for most current Linux distributions. GRUB2 is the program which makes the computer just smart enough to find the operating system kernel and load it into memory. Because it is easier to write and say GRUB than GRUB2, I may use the term GRUB in this document but I will be referring to GRUB2 unless specified otherwise.

GRUB2 provides the same boot functionality as GRUB Legacy but GRUB2 also provides a mainframe-like command-based pre-OS environment and allows more flexibility during the pre-boot phase. GRUB2 is configured with /boot/grub2/grub.cfg.

GRUB has been designed to be compatible with the multiboot specification which allows GRUB to boot many versions of Linux and other free operating systems; it can also chain load the boot record of proprietary operating systems. GRUB can also allow the user to choose to boot from among several different kernels for any given Linux distribution. This affords the ability to boot to a previous kernel version if an updated one fails somehow or is incompatible with an important piece of software. I have used this capability several times over the years.

GRUB can be configured using the /boot/grub/grub.conf file. The configuration of GRUB or GRUB2 and the use of GRUB2 commands is outside the scope of this article. As mentioned in the POST section, at the end of POST, BIO/EUFI searches the attached disks for a boot record, located in the GPT. BIO/EUFI loads the first one it finds into memory and then starts execution of the boot record.

The GUID Partition Table

Before going further, let’s look at the GUID partition table that replaces the legacy MBR partition table. The GUID Partition Table (GPT) shown in Figure 2 is a relatively new, modern standard for disk partition tables. GPT is part of the Unified Extensible Firmware Interface (UEFI) specification. Designed both for much greater disk sizes and systemic redundancy, it is larger than the legacy MBR, so it supports much larger storage devices up to 9.44 zetabytes, or 9.44 × 1021.

Figure 2: The GUID partition table takes the entire first cylinder of the storage device and provides for a large number of partitions. The partition table provides pointers to up to 128 partition entries. Each entry defines a partition in the data area of the storage device.

GPT does use an MBR in the first sector of the disk although it is partially designed as a protective structure to provide an identifier so that system tools that only recognize MBR partitions don’t see the drive as an empty storage device.

You can determine whether a storage device was created with a legacy MBR or GPT using the gdisk command in Figure 3. The nvme storage device I scanned is formatted GPT.

root@david:~# gdisk /dev/nvme0n1
GPT fdisk (gdisk) version 1.0.10

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Figure 3: The disk I scanned on my primary workstation has a GPT partitioning table scheme.

One thing to be aware of when scanning drives with gdisk is the instance in which all of the partition table formats show “not present.” If that occurs, it is most likely because the storage device is an LVM physical volume (PV).

Master Boot Record (MBR)

The MBR is the first stage of the boot loader. There are three boot loaders used by most Linux distributions, GRUB, GRUB2, and LILO. GRUB2 is the newest and is used much more frequently these days than the other older options which are all considered to be obsolete. The MBR — whether GPT or legacy version — is the starting point for Linux boot and startup.

The GUID MBR contains a small executable image, which is installed from the boot.img file. This boot image is loaded into RAM and locates the core.img file which is the next stage of the boot process. Storage devices that use the legacy MBR and partition table instead of the GUID MBR and partitioning structure can still use GRUB2 but the locations of some files are different. When using the GPT, the core.img file is located in the BIOS boot partition.

The core.img is smart enough to locate and load the files for the final stage of GRUB. All of the files for GRUB stage 2 are located in the /boot/grub2 directory and several subdirectories. GRUB2 does not have an image file. Instead, it consists mostly of runtime kernel modules that are loaded as needed from the /boot/efi/EFI directory.

The function of GRUB2 stage 2 is to locate and load a Linux kernel into RAM and turn control of the computer over to the kernel. The kernel and its associated files are located in the /boot directory. The kernel files are identifiable as they are all named starting with vmlinuz. You can list the contents of the /boot directory to see the currently installed kernels on your system.

GRUB supports booting from one of a selection of Linux kernels. The Red Hat package manager, DNF, supports keeping multiple versions of the kernel so that if a problem occurs with the newest one, an older version of the kernel can be booted. By default, GRUB provides a pre-boot menu of the installed kernels, including a rescue option and, if configured, a recovery option.

Kernel

All of the kernels are in a self-extracting, compressed format to save space. The kernels are located in the /boot directory, along with an initial RAM disk image, and device maps of the hard drives.

After the selected kernel is loaded into memory and begins executing, it must first extract itself from the compressed version of the file before it can perform any useful work. Once the kernel has extracted itself, it loads systemd, which is the replacement for the old SysV init program, and turns control over to it.

This is the end of the boot process. At this point, the Linux kernel and systemd are running but unable to perform any productive tasks for the end user because nothing else is running.


The startup process

The startup process follows the boot process and brings the Linux computer up to an operational state in which it is usable for productive work.

systemd

systemd is the mother of all processes and it is responsible for bringing the Linux host up to a state in which productive work can be done. Some of its functions, which are far more extensive than the old init program, are to manage many aspects of a running Linux host, including mounting filesystems, and starting and managing system services required to have a productive Linux host. systemd tasks that are not related to the startup sequence are outside the scope of this article.

First, systemd mounts the filesystems as defined by /etc/fstab, including any swap files or partitions. At this point, it can access the configuration files located in /etc, including its own. It uses its configuration file, /etc/systemd/system/default.target, to determine which state or target, into which it should boot the host. The default.target file is only a symbolic link to the true target file. For a desktop workstation, this is typically going to be the graphical.target, which is equivalent to runlevel 5 in the old SystemV init. For a server, the default is more likely to be the multi-user.target which is like runlevel 3 in SystemV. The emergency.target is similar to single user mode.

Note that targets and services are systemd units.

Figure 4 (below) is a comparison of the systemd targets with the old SystemV startup runlevels. The systemd target aliases are provided by systemd for backward compatibility. The target aliases allow scripts—and many sysadmins like myself—to use SystemV commands like init 3 to change runlevels. The SystemV commands are forwarded to systemd for interpretation and execution.

SystemV Runlevelsystemd targetsystemd target aliasesDescription
halt.targetHalts the system without powering it down.
0poweroff.targetrunlevel0.targetHalts the system and turns the power off.
Semergency.targetSingle user mode. No services are running; filesystems are not mounted. This is the most basic level of operation with only an emergency shell running on the main console for the user to interact with the system.
1rescue.targetrunlevel1.targetA base system including mounting the filesystems with only the most basic services running and a rescue shell on the main console.
2runlevel2.targetMultiuser, without NFS but all other non-GUI services running.
3multi-user.targetrunlevel3.targetAll services running but command line interface (CLI) only.
4runlevel4.targetUnused.
5graphical.targetrunlevel5.targetmulti-user with a GUI.
6reboot.targetrunlevel6.targetReboot
default.targetThis target is always aliased with a symbolic link to either multi-user.target or graphical.target. systemd always uses the default.target to start the system. The default.target should never be aliased to halt.target, poweroff.target, or reboot.target.
Figure 4: Comparison of SystemV runlevels with systemd targets and some target aliases.

Each target has a set of dependencies described in its configuration file. systemd starts the required dependencies. These dependencies are the services required to run the Linux host at a specific level of functionality. When all of the dependencies listed in the target configuration files are loaded and running, the system is running at that target level.

systemd also looks at the legacy SystemV init directories to see if any startup files exist there. If so, systemd used those as configuration files to start the services described by the files. The deprecated network service is a good example of one of those that still use SystemV startup files in Fedora.

The sysinit.target and basic.target targets can be considered as checkpoints in the startup process. Although systemd has as one of its design goals to start system services in parallel, there are still certain services and functional targets that must be started before other services and targets can be started. These checkpoints cannot be passed until all of the services and targets required by that checkpoint are fulfilled.

So the sysinit.target is reached when all of the units on which it depends are completed. All of those units, mounting filesystems, setting up swap files, starting udev, setting the random generator seed, initiating low-level services, and setting up cryptographic services if one or more filesystems are encrypted, must be completed, but within the sysinit.target those tasks can be performed in parallel.

The sysinit.target starts up all of the low-level services and units required for the system to be marginally functional and that are required to enable moving on to the basic.target.

Figure 5 is copied directly from the bootup man page. It shows the general sequence of events during systemd startup and the basic ordering requirements to ensure a successful startup.

   local-fs-pre.target
            |
            v
   (various mounts and  (various swap  (various cryptsetup
    fsck services...)    devices...)       devices...)      (various low-level  (various low-level
            |                 |                 |            services: udevd,    API VFS mounts:
            v                 v                 v            tmpfiles, random    mqueue, configfs,
     local-fs.target     swap.target    cryptsetup.target   seed, sysctl, ...)     debugfs, ...)
            |                 |                 |                   |                    |
            \_________________|________________ | __________________|___________________/
                                               \|/
                                                v
                                         sysinit.target
                                                |
             __________________________________/|\_______________________________________
            /                  |                |                   |                    \
            |                  |                |                   |                    |
            v                  v                |                   v                    v
        (various           (various             |               (various          rescue.service
       timers...)          paths...)            |              sockets...)               |
            |                  |                |                   |                    v
            v                  v                |                   v              rescue.target
      timers.target      paths.target           |            sockets.target
            |                  |                |                   |
            v                  \_______________ | __________________/
                                               \|/
                                                v
                                          basic.target
                                                |
             __________________________________/|                         emergency.service
            /                 |                 |                                 |
            |                 |                 |                                 v
            v                 v                 v                          emergency.target
        display-       (various system   (various system
    manager.service        services          services)
            |            required for           |
            |           graphical UIs)          v
            |                 |          multi-user.target
            |                 |                 |
            \________________ | ________________/
                             \|/
                              v
                     graphical.target

Figure 5: The systemd startup map.

After the sysinit.target is fulfilled, systemd next starts the basic.target, starting all of the units required to fulfill it. The basic target provides some additional functionality by starting units that re required for the next target. These include setting up things like paths to various executable directories, communication sockets, and timers.

Finally, the user-level targets, multi-user.target or graphical.target can be initialized. Notice that the multi-user.target must be reached before the graphical target dependencies can be met.

The underlined targets in Figure 5, are the usual startup targets. When one of these targets is reached startup has completed. If the multi-user.target is the default, you should see a text mode login on the console. If graphical.target is the default, then you should see a graphical login; the specific GUI login screen you see will depend on the default display manager you use.

Issues

I once had a need to change the default boot kernel on a Linux computer that used GRUB2. I found that some of the commands did not seem to work properly for me, or that I was not using them correctly. I am not yet certain which was the case, and need to do some more research.

The grub2-set-default command did not properly set the default kernel index for me in the /etc/default/grub file so that the desired alternate kernel did not boot. So I manually changed /etc/default/grub from GRUB_DEFAULT=saved to GRUB_DEFAULT=2 where 2 is the index of the installed kernel I wanted to boot. Then I ran the command grub2-mkconfig > /boot/grub2/grub.cfg to create the new grub configuration file. This circumvention worked as expected and booted to the alternate kernel.

Final Thoughts

GRUB2 and the systemd init system are the key components in the boot and startup phases of most modern Linux distributions. Despite the fact that there has been controversy surrounding systemd especially, these two components work together smoothly to first load the kernel and then to start up all of the system services required to produce a functional Linux system.

Although I do find both GRUB2 and systemd more complex than their predecessors, they are also just as easy to learn and manage. Your computer’s online manual (“man”) pages include a great deal of information about systemd, and freedesktop.org has the complete set of systemd man pages online. Refer to the resources below for more links.

Most of the time the difference between MBR and GPT is not relevant to the operation of Linux hosts or the task of problem solving. The function of both is to partition storage devices into usable chunks and to provide a tiny bit of code to provide a transition between BIOS/UEFI hardware boot and the main portion of the GRUB bootloader.


Additional resources


  1. Red Hat Documentation for RHEL 8 has a good, succinct description of disk partitioning. ↩︎