Optimizing the Linux Kernel

Kernel Compilation 101

Many of the best Linux tweaks require you to recompile the kernel. The steps might vary depending on your distro, but I'll briefly outline a universal recipe.

The first step is to get the source tree of the Linux kernel. You can grab the tree right from kernel.org to get a pure, vanilla kernel, which is perfectly fine depending on your needs. Another option is to get the source code used in your Linux distribution. This way, you'll also get specific patches that your vendor decided to apply to the kernel. As an example, you can download the source code for Fedora and recent RHEL versions with:

$ dnf download --source kernel
$ rpm -ivh kernel*src.rpm #
$ rpmbuild -bp --target=$(uname -m) ~/rpmbuild/SPECS/kernel.spec

Your kernel source tree with all patches applied will appear at ~/rpmbuild/BUILD/. The Ubuntu family allows you to get the kernel source via apt-get:

$ apt-get source linux-image-unsigned-$(uname -r)

The kernel source tree will then emerge under the debian subdirectory.

You will need to obtain the kernel configuration file that specifies what exact part of the kernel you want to build. This file is named .config, and it must reside under the main directory of the kernel source. You can generate .config by explicitly running the kernel configuration menu, as follows:

$ make menuconfig # ncurses-based interface
$ make xconfig # Qt-based interface

Also, you can take the configuration of the currently running kernel and use it as a template. You'll find the file with the current configuration under /boot:

$ cp /boot/config-$(uname r) kernel_source_dir

Now, you can apply extra patches to the kernel source tree (although it is perfectly fine to apply patches before running a configuration command as well). Many Linux vendors use patches to customize their kernel (for example, fix building for certain compilers, fortify security features, add support for extra hardware). Some kernel modules are not included in the default kernel tree and therefore are only available as patches. A good example is the Reiser4 file system, which consists of user-level utilities and the kernel module. The kernel module is available as a .patch file. Place it inside the kernel source directory and apply it as follows:

$ patch -p1 <filename.patch

Next you can run the configuration dialog and enable new items. Finally, build the kernel with make, although there are few more things to consider. First, keep in mind that building a Linux kernel takes a while even on high-performance machines. It is possible to save some time by running make with several threads (one per each CPU core), which will saturate the CPU load and make the process complete sooner. Second, if you plan to keep using the system while the kernel is compiling, it is important to maintain the system responsiveness by lowering the priority of the compilation task with ionice. One approach would be something like:

$ nice ionice -c idle make -j$(nproc -all)

Don't forget to build loadable modules as well:

$ nice ionice -c idle make modules -j$(nproc -all)

Finally, install the kernel with:

$ sudo make modules_install
sudo make install

In most Linux distros, you won't need to update the GRUB 2 configuration manually, and your kernel should be available for booting right away. If it is not, try the following command:

$ sudo grub2-mkconfig -o /boot/grub*/grub.cfg

The kernel's makefile also supports several extra packaging targets for several mainstream Linux distributions. For instance, you might want to try make rpm-pkg or make deb-pkg in order to get some helpful installation kernel packages for your system.

Going with a Fully-Preemptible Kernel

In order to achieve improved system responsiveness, you could rebuild the Linux kernel with full preemption enabled. This term needs additional explanation. Preemption defines the kernel behavior when it needs to distribute CPU time between processes that have different priorities. By default, the vanilla Linux kernel is not preemptive, which means that it will always first serve a high-priority process and only then serve a low-priority process. The goal of this default behavior is to keep the count of kernel context switches low and therefore maintain higher throughput of high-priority processes. This model is considered good for server appliances, where the performance of network services is crucial.

Preemptive and fully preemptive modes prioritize the speed of kernel responses over performance of individual processes. This approach introduces certain loss in the per-process throughput but in return eliminates long queues of low-priority processes that would otherwise need to wait longer. As such, a preemptive kernel plays best in a desktop multitasking environment and is a key role in those subjective "snappiness" and 'instant response" effects preferred by many users. Such a system is immune to unpredictable delays that can be encountered during syscalls, so it might be better suited for embedded or real-time tasks. To make the kernel fully preemptive, you need to open the Linux kernel configuration (e.g., make xconfig), go to General Setup and, under the preemption model, choose Preemptible Kernel (Low-Latency Desktop), as shown in Figure 3.

Figure 3: For years, the Linux kernel has included a special setting for better desktop responsiveness. You need to recompile the kernel in order to use it.

Another kernel setting that contributes to a more responsive workflow is the interrupt frequency timer set in Hz. The timer interrupt is the default interval at which the Linux kernel serves system calls. The higher this value, the better the timer resolution and the smaller the latencies between syscalls and actual context switches. The default configuration of the Linux kernel tends to keep the timer frequency low, so it's a good idea to increase it. Go to Processor type and features | Timer frequency and choose a higher value. It is a good idea to pick 500Hz or 1000Hz over 100Hz or 250Hz in order to receive a small but tangible minimum frame rate improvement in gaming and faster switching for productivity applications. Find any kernel setting you decided to change using the graphical front-end to Menuconfig (Figure 4).

Figure 4: Consider using the Qt-based graphical UI for more comfort and control when customizing the kernel configuration.

Faster Boot Times from the Kernel Perspective

Optimizing the OS boot time is not directly a kernel-side business, but it is a complex task that involves the kernel. I will leave aside the system service part (which you can examine with $ systemd-analyze -blame) and look at the kernel part. The first consideration is to take full advantage of the zstd compression method. zstd stands for Zstandard, the open source algorithm developed by Facebook. zstd provides good compression ratios, although it is not as competitive for large file sizes. Where it does set a record is compression and decompression speeds. zstd archives are super-speedy to unpack, which is something you can benefit from when booting the compressed Linux image (vmlinuz). The Linux kernel supports zstd for the main image since version 5.9. You can also archive modules using zstd if your kernel is 5.13 or newer.

Another boot-related tweak is to ditch GRUB 2 entirely and boot the kernel directly from UEFI firmware. Obviously this technique requires a UEFI-enabled system, which is not a problem if your hardware is relatively new. As a prerequisite, manually copy the base Linux image and the initramfs image to a different location, as follows:

$ sudo cp /boot/vmlinuz-$(uname -r) /boot/efi/EFI/vmlinuz.efi
$ sudo cp /boot/initramfs-$(uname -r) /boot/efi/EFI/initrd.img

If you had /boot at /dev/sda1 and / at /dev/sda3, your EFI boot entry would look like this:

$ sudo efibootmgr --create --disk /dev/sda --part 1 --label "your_label" -u --loader '\efi\vmlinuz.efi'
"root=/dev/sda3 initrd=/efi/initrd.img resume=/dev/sda3 splash=silent quiet init=/lib/systemd/systemd

Essentially, you need to make sure that the init option points to the right location because your Linux distro might have a different systemd setup. After you ensure that your EFI boot entry is working, you can add extra boot parameters, including the parameters described earlier in this article.

Change the boot order with # efibootmgr -o and remove stale boot entries with # efibootmgr -b E -B, where E is the last character of the desired boot entry (i.e., Boot000E).

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Working with the Kernel

    If you work with third-party hardware drivers, or even if you just need to fix a broken system, someday you might need to upgrade the Linux kernel.

  • Kernel Tips

    Worried about a recent security exploit? Want to take advantage of a new hardware feature? You don’t need to be a Linux expert to patch and compile the Linux kernel. We'll show you how to get started.

  • Performance Tweaks

    If you are looking for ways to speed up your Linux, consider this collection of curated performance tweaks.

  • Compiling the Kernel

    While not a requirement, compiling the Linux kernel lets you add or remove features depending on your specific needs and possibly make your kernel more efficient.

  • Tutorials – Build the Linux Kernel

    Get a super-customized Linux installation by configuring and compiling the kernel with just the features you need.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More