The startup process using systemd





Last Updated on 04/24/2020 by dboth

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. This article describes the startup process using the new systemd.

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 it’s functions, which is 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. Any of systemd’s 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 SystemV. 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.

Table 1, 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. Of course the SystemV commands are forwarded to systemd for interpretation and execution.

systemd targetsSystemV runleveltarget aliasesDescription
default.target

This 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.
graphical.target5runlevel5.targetMulti-user.target with a GUI.

4runlevel4.targetUnused. Runlevel 4 was identical to runlevel 3 in the SystemV world. This target could be created and customized to start local services without changing the default multi-user.target.
multi-user.target3runlevel3.targetAll services running but command line interface (CLI) only.

2runlevel2.targetMultiuser, without NFS but all other non-GUI services running.
rescue.target1runlevel1.targetA basic system including mounting the filesystems with only the most basic services running and a rescue shell on the main console.
emergency.targetS
Single 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.
halt.target

Halts the system without powering it down.
reboot.target6runlevel6.targetReboot
poweroff.target0runlevel0.targetHalts the system and turns the power off.
Table 1: 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 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.

Figure 1, below, 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.

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.

                                           cryptsetup-pre.target
                                                     |
   (various low-level                                v
    API VFS mounts:                 (various cryptsetup devices...)
    mqueue, configfs,                                |    |
    debugfs, ...)                                    v    |
    |                                  cryptsetup.target  |
    |  (various swap                                 |    |    remote-fs-pre.target
    |   devices...)                                  |    |     |        |
    |    |                                           |    |     |        v
    |    v                       local-fs-pre.target |    |     |  (network file systems)
    |  swap.target                       |           |    v     v                 |
    |    |                               v           |  remote-cryptsetup.target  |
    |    |  (various low-level  (various mounts and  |             |              |
    |    |   services: udevd,    fsck services...)   |             |    remote-fs.target
    |    |   tmpfiles, random            |           |             |             /
    |    |   seed, sysctl, ...)          v           |             |            /
    |    |      |                 local-fs.target    |             |           /
    |    |      |                        |           |             |          /
    \____|______|_______________   ______|___________/             |         /
                                \ /                                |        /
                                 v                                 |       /
                          sysinit.target                           |      /
                                 |                                 |     /
          ______________________/|\_____________________           |    /
         /              |        |      |               \          |   /
         |              |        |      |               |          |  /
         v              v        |      v               |          | /
    (various       (various      |  (various            |          |/
     timers...)      paths...)   |   sockets...)        |          |
         |              |        |      |               |          |
         v              v        |      v               |          |
   timers.target  paths.target   |  sockets.target      |          |
         |              |        |      |               v          |
         v              \_______ | _____/         rescue.service   |
                                \|/                     |          |
                                 v                      v          |
                             basic.target         rescue.target    |
                                 |                                 |
                         ________v____________________             |
                        /              |              \            |
                        |              |              |            |
                        v              v              v            |
                    display-    (various system   (various system  |
                manager.service     services        services)      |
                        |         required for        |            |
                        |        graphical UIs)       v            v
                        |              |            multi-user.target
   emergency.service    |              |              |
           |            \_____________ | _____________/
           v                          \|/
   emergency.target                    v
                                graphical.target 

Figure 1: The systemd startup map. Note that this figure has been updated from the most recent version of the Fedora 31 bootup man page.

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 1, are the usual startup targets. When one of these targets is reached, then startup has completed. If the multi-user.target is the default, then 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 upon the default display manager.

Managing SystemD targets

The SystemD targets (runlevels) for a Linux host can be managed easily from the command line using the systemctl command. For example, the default target can be viewed using the following command which shows that the default target is the graphical user interface.

[root@david ~]# systemctl get-default
graphical.target

Although this is the default target, this is not necessarily the currently active target. I cannot find a systemctl command that will directly or easily display the current active target so I recommend sticking with a couple of the old standbys even though they may be obsolete.

[root@david ~]# who -r
         run-level 5 2018-06-26 11:24
[root@david ~]

# runlevel N 5

Both of the above commands show runlevel 5 — graphical.target — as the current runlevel. I prefer the who -r command because it shows the date and time that the current target was reached. The runlevel command shows the previous runlevel but I am very seldom interested in knowing that. In the example above, the previous runlevel is given as “N” for none which means that the system was brought to runlevel 5 (graphical.target) directly from the “off” state.

To set the default target — the target that the system will run in after the next startup — use the set-default sub-command as shown in the following example.

[root@david ~]# systemctl get-default ; systemctl set-default multi-user.target ; systemctl get-default
graphical.target
Removed /etc/systemd/system/default.target.
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.
multi-user.target

The preceding example uses multiple commands separated by semi-colons (;) to first display the current default target, to change the default target to the multi-user.target, and then to display the newly set default target as verification.

The running target can be changed on the fly without a reboot using the isolate sub-command. This may terminate running programs so it should be used sparingly and only when absolutely necessary.

The systemd program and systemctl command both have a lot more capabilities than we have discussed here but that are well beyond the scope of this document.

Issues

I recently 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, 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 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.

Conclusions

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 GNU/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. The man pages have a great deal of information about systemd, and freedesktop.org has the complete set of systemd man pages on line. Refer to the Resources section, below, for more links.

Resources

WikiPedia: GNU GRUB
GNU.org: GNU GRUB Manual
WikiPedia: Master Boot Record
WikiPedia: Multiboot specification
Wikipedia: systemd
Freedesktop.org: systemd bootup process
Freedesktop.org: systemd index of man pages