How to compile vanilla Linux kernel from source on Fedora

The kernel is the most important component of an operating system: among the other things, it provides support for different types of hardware and manages resource allocations.

Linux is a monolithic kernel: although its functionalities can be included statically or built and loaded as separate modules, it always runs as a “single piece” in the same address space. In this tutorial we will see how to download, compile and install a vanilla Linux kernel. The instructions provided should work on all Linux distributions, however this guide is focused on compiling the kernel on a Fedora system.

In this tutorial you will learn:

  • How to configure, compile and install a vanilla Linux kernel
  • How to package the compiled kernel and its modules

linux-kernel-ncurses-config-interface

The ncurses-based configuration menu for the Linux kernel

Software Requirements and Conventions Used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Fedora
Software
  • gcc
  • flex
  • make
  • bison
  • openssl-devel
  • elfutils-libelf-devel
  • ncurses-devel (needed to use the ncurses-based menu to configure the kernel)
  • qt-devel (needed to use the Qt graphical interface to configure the kernel)
  • rpm-build (needed to package the kernel in rpm format)
Other Root permissions to install needed dependencies and the compiled kernel
Conventions # – requires given linux commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux commands to be executed as a regular non-privileged user

Installing the dependencies

To be able to compile the Linux kernel from source we need to install some packages in our system:

$ sudo dnf install gcc flex make bison openssl-devel elfutils-libelf-devel


The ones above are only the ‘core’ packages we need. To invoke specific configuration targets some extra packages must be installed: the ncurses-devel and qt-devel packages, for example, are needed to configure the kernel making use, respectively, of the ncurses-based and the Qt graphical interface, while the rpm-build package is needed to build an rpm containing the compiled kernel.

Downloading the source tarball

As a first thing, we need to obtain the tarball containing the latest stable Linux kernel sources. We can download and extract the tarball with just one command:

$ curl https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.5.tar.xz|tar -xJ

At the end of the download process, inside our current working directory, we should find a new the folder containing the kernel source code. We need to enter it, in this case:

$ cd linux-5.1.5

At this point we can configure the kernel. In the next paragraph we will take a look at the most common configuration targets used to accomplish the task.

Configuring the kernel

There are various ways we can configure the kernel, and they correspond to different configuration targets. When a configuration target is invoked, if a file named .config containing a valid kernel configuration is found in the sources directory, it is used as a starting point for the setup. This makes possibile to update or modify an already existent configuration, perhaps the one that comes with the kernel installed by default in our distribution, (it can be found inside the /boot directory, named after the kernel in use).

If the file is not found, the configuration will start from scratch and the .config file will be generated once we save our setup. Let’s see some of the configuration targets we can use:

config

If this target is invoked, the user is prompted to configure the kernel by answering a series of questions, in the following fashion:

*
* Linux/x86 5.1.5 Kernel Configuration
*
*
* Compiler: gcc (GCC) 9.1.1 20190503 (Red Hat 9.1.1-1)
*
*
* General setup
*
Compile also drivers which will not load (COMPILE_TEST) [N/y/?]

menuconfig

This target uses a nice and user-friendly ncurses interface to let us generate or update the kernel configuration. As said before, to be able to use this interface the ncurses-devel package must be installed in the system.


linux-kernel-ncurses-config-interface

The ncurses-based configuration inteface


Using this interface, we can press the h key when highlighting a specific option to obtain information and suggestions about it:


ncurses-menu-help

Obtaining information about an option is just a matter of selecting it and press the h key Pressing Y on an option will include it into the kernel statically (the option will be marked with a *), pressing N will exclude it, and pressing the M key will include it as a module (the option will be marked with an M). To search for a specific option, we can use the / key and provide the string or regex to search for:

linux-kernel-ncurses-option-search

The ncurses interface to search for a specific option

xconfig

By invoking this make target it’s possibile to configure the kernel via a graphical interface based on the Qt toolkit if the qt-devel package is installed in the system.


linux-kernel-qt-config-interface

The Qt-based configuration interface

oldconfig

This target it’s useful when we want to use an already existing kernel configuration as a starting point. When we invoke this target, we are prompted to configure only the features available in the kernel we are configuring but not included in the original configuration file.

localmodconfig

Invoking this target will generate or update a new kernel configuration file on the base of the modules currently loaded on the system. Only them will be included in the configuration, the others will be disabled. This can be used as a quick way to obtain a tailored kernel based on the current state of a machine.

localyesconfig

This target works similarly to localmodconfig with one big difference: the functionalities provided by the modules currently loaded in the system will be statically included into the kernel.

Compiling and installing the kernel

Once we finished configuring the kernel, we can compile the source code. All we have to do is to run:

$ make


The operation may take a while, depending on the features we decided to include in the kernel. To speed up the process we can run make with the -j option and specify the number of jobs to run simultaneously: a value often used for this option is the number of logical CPU cores + 1. On a machine with 4 logical cores, we would therefore run:

$ make -j5

Once compiled, to install the kernel we can simply run:

$ sudo make install

The kernel core files will be copied inside the /boot directory. To compile and install the kernel modules, instead, we can run:

$ sudo make modules_install

The kernel modules will be installed in a directory under /lib/modules named after the kernel version. Finally, for the new kernel to be available and selectable at boot, we must regenerate the grub configuration:

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

Packaging the kernel

Instead of installing the compiled kernel directly as we did above, in order to be able to manage its installation and removal via the system package manager, we can create an rpm package. To accomplish the task we must use one between the rpm-pkg and binrpm-pkg targets. The first will build both the source and binary RPM packages, the second only the binary one. For this target to run correctly, the rpm-build package must be installed. To build only the binary rpm package, we would run:

$ make binrpm-pkg

If the operations are performed without errors, the rpmbuild directory tree will be created in our home directory. The built rpm package will be available in a subdirectory of ~/rpmbuild/RPMS named after the architecture of the system.

Another option is to package the kernel and its modules inside a compressed tarball, by using one between the targz-pkg, tarbz2-pkg and tarxz-pkg targets, depending on the compression we want to use. The tarball will be created inside the kernel source directory.



Conclusions

In this tutorial we learned to know why Linux is called a monolithic kernel, and how its components can be configured statically or as modules. We saw how to download a vanilla kernel and the various methods we can use to configure it. Finally, we saw how to compile it, package it and install it in our system. One last advice: if you decide to re-compile the kernel, it’s always a good idea to invoke one of the cleaning targets before proceeding:

  • clean: Removes most generated files but keep the config and enough build support to build external modules
  • mrproper: Removes all generated files + config + various backup files
  • distclean: Executes mrproper and also removes editor backup and patch files