Motivation: Achieving Real-Time (RT) capability
To build time-critical programs that requires a high-level of responsiveness on Linux, the most important step is to select and use the correct Linux kernel. This is the goal of this note.
The Linux kernel is frequently built and released. The latest version of the generic kernel build as I write this note is 5.9-rc3. Howerver, this kernel is not suitable for RT applications: Its scheduler optimizes for total throughput and thus prevent any single process from being the absolute higest priority. We will need either a Fully Preemptible Kernel or at least a low-latency one.
Obtaining generic and low-latency kernels
Fully-Preemptible kernels are only used for time-critical applications such as device and robot control. If the highest level of responsiveness is not required, one can look at mainline generic or low-latency kernels at the kernel archive. The difference between the kernel variants–generic, low-latency and fully preemptible–are explained here and here.
Both generic and low-latency kernels are frequently built and
released on the kernel archieve. First, find your architecture by
arch. Note that x86_64 and amd64 are the same
architectures. Second, install pre-compiled kernels. We download
.deb files. For example to install the generic 5.8.0
kernel, download the following files:
linux-headers-5.8.0-050800_5.8.0-050800.202008022230_all.deb linux-headers-5.8.0-050800-generic_5.8.0-050800.202008022230_amd64.deb linux-image-unsigned-5.8.0-050800-generic_5.8.0-050800.202008022230_amd64.deb linux-modules-5.8.0-050800-generic_5.8.0-050800.202008022230_amd64.deb
Install these debian files with
dpkg. The new kernel is now installed.
Compiling a Fully Preemptible Linux Kernel
Fully-Preemptible Linux kernels are not available as precompiled binaries; we need to compile from source.
First, choose a RT patch from this mirror. I chose version 4.14.78-rt47 when writing this note. The lastest patch as of the time of this writing is 4.19-rt3, unfornately does not work on my machine running Ubuntu 16.04.
Note the kernel version you have choosen, download the corresponding kernel source code from this mirror. Both steps can be done via the terminal as below.
cd ~/Downloads # download patch wget https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/4.14/patch-4.14.78-rt47.patch.xz # download kernel wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.14.78.tar.xz
Unzipped the archives and patch the kernel with the RT patch.
tar xvf linux-4.14.78.tar.xz cd linux-4.14.78 xzcat ../patch-4.14.78-rt47.patch.xz | patch -p1 --verbose
We now enable the option Fully Preemptible Kernel (RT). First, install two necessary dependencies.
sudo apt-get install libncurses-dev libssl-dev
Configure the compilation by doing
You will see a graphical user interface (GUI) show up in the terminal. This GUI contains configurations for compiling the Kernel. The bit that we need is called Preemption Model:
- Navigate using arrow keys to Processor type and feature, then press Enter;
- look for Preemption Model, press Enter;
- choose Fully Preemptible Kernel (RT) option;
- return to the first screen by pressing Esc multiple times;
- save .config file, then exit.
Compile the Kernel and install.
make -j20 sudo make modules_install -j20 sudo make install -j20
Remark for 5.x version kernels
The Fully Preemptible Kernel (RT) option seems to have been shifted from Processor type and feature to General setup. Thanks Iain!
Configure the kernel
Now that the kernel is compiled and installed, we need to tell the
bootloader about it. First, check that the new kernel can be found in
/boot, then run
sudo update-grub to generate new grub config file
If you want to set a default kernel at boot or prevent the user from
selecting a different kernel, you will need to configure the grub
configuration file. Simple configuration is the easiest way to
achieve this. Edit
/etc/default/grub then rerun
update-grub to regenerate the configuration. To see documentation
on the entries, use the command below for the documentation.
info -f grub -n 'Simple configuration'
GRUB_DEFAULT specifies the default entries. If there are
sub menuentry, then the value of
GRUB_DEFAULT must have the form
`x>y`, where x is the n-th main entry, and y is the n-th sub
entry. This is my current default grub configuration.
GRUB_DEFAULT="1>2" GRUB_TIMEOUT_STYLE="countdown" GRUB_TIMEOUT="0" GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMDLINE_LINUX=""
Try it out
And that’s it. Reboot, remember to choose the patched Kernel in
Advanced Boot Setting, and enjoy! At this point
uname -a should
print out the kernel name.
Some useful reads I came across while working on this: