This will be a walkthrough of steps for making custom linux kernel modules, interacting with kernel space and understanding how proc file system works in linux. We will be completing the programming-project of Chapter-2 of Operating System Concepts : 10th-edition
The code in this repository belongs to the book Operating System Concepts : 10th-edition. Though there are minor modifications pertaining to breaking changes in newer kernel versions!
See complete References below.
- Introduction
- Setting up workspace
- Our Workflow
- Running The Project Module(s)
- What's next
- References(s)
Linux operating system follows a monolithic architecture, so it's Kernel is called as monolothic, which means most of the operating system functions from Kernel space, this increases the speed of execution, but makes the Kernel bulky and takes away modularity. So, in modern Kernels, this problem is addressed by linux kernel modules.
Image Source : eBPF - Rethinking the Linux Kernel
Linux Kernel Modules are object files which can be laoded/linked at Runtime, thus extending the functionality of Kernel when needed, and similarly can be unloaded/unlniked when no longer needed.
Usually Linux Kernel Modules are written in C programming language
Image Source : How to load or unload a Linux kernel module
In Linux, Proc file system contains runtime information of system and acts as a source of communcation between user and kernel spaces. It is a virtual or pseudo file system which means the files listed in this aren't really stored permanently, this file system is created and updated at runtime, while it gets destroyed as system is turned off.
Image Source : Slide 38 : Lecture 5 proc file system
Compiling a Linux Kernel Module Differs from compiling a normal C-program, since Kernel Modules live in and interact with Kernel space, so the headers used are different and also they must be compiled with same options as Kernel to that make them compatible. Now all this is achieved through use of Makefile and make command!
In general a Makefile is like a guide for compiling a program. When dealing with a simple program with limited files, there's not much inter-dependency and we can manually compile them.
But in case of larger projects with many files and inter-dependencies it become neccasary to use a Makefile as :
- It defines the order in which files must be compiled, so as to resolve referencing conflicts!
- In case of any changes in files, it only recompiles required portions thus saving precious time and compuational power.
For a module name moduleName
, our Makefile will be of form:
obj-m += moduleName.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
To compile the Module
sudo make
Here
make
, will automatically identifyMakefile
and run it.
moduleName.ko
will be the Kernel module, we are concerned with.
To destroy the built module
sudo make clean
obj-m += moduleName.o
obj-m
specifies, we need a module as output, whilemoduleName.o
refers to the source object file, in our case we have moduleName.c source file, so object file will automatically be generated from it!
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
all
is the defaulttarget
for a Makefile, which means running commandmake
alone, will triggerall
.
Note : We are recursively calling make again inside Makefile.
-C option changes the directory of Makfile to/lib/modules/$(shell uname -r)/build
, which contains the Makefile used to build the Kernel, thus effectively using it's options.
WhereasM
is a variable, which causes that makefile to move back into your module source directory before trying to build the modules target.
And finallymodules
is the target we call, which means we are trying to make a module.
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
The only difference here is we are using target clean, so
sudo make clean
will invoke clean from Makefile of Kernel and clean modules inM
directory.
- VM Virtual Box to install linux, which works like a virtual machine for a given operating system.
- Ubuntu distrbution of Linux as Operating system.
Note : Althugh other distributions may work with instructions given in this tutorial, it's advisable to adhere strictly to specifications!
- Download Virtual Box for your Operating system from here.
- Download Ubuntu 22.04-LTS from here.
- Follow this tutorial to make a new Ubuntu machine using VMbox.
- Boot up your Ubuntu system in VMbox and open a terminal
- Install
build-essential
&kmod
packages
sudo apt-get install build-essential kmod
kmod
package provides commandslsmod
,insmod
andrmmod
which we'll use to list,insert and remove kernel modules respectively.
build-essential
package provides us withgcc compiler
which we'll use to compile C-files, andmake
command to build kernel modules.
- Install
kernel-headers
for your Kernel version
sudo apt-get update
sudo apt-get install linux-headers-$(uname -r)
apt-get update
fetches latest avaliable versions of packages available to compare against current ones, more info here.
uname -r
refers tounix-name release
which essentially is the currently installed kernel.
linux-headers-$(uname -r)
refers to headers corresponding to current kernel version, modules already available in kernel were compiled using these headers and hence, these modules no longer need the headers, but now to compile our own modules, we need these headers!
Each of our module will live in a separate directory named moduleName
, where moduleName
also is the name of the module we are dealing with, and at starting point before compilation , we would have two files inside moduleName
directory :
moduleName.c
=> the source code of module.Makefile
=> the Makefile for current module.
For example : we have a module named
simple
, so we would have a directory namedsimple
, and inside it we will havesimple.c
andMakefile
.
- navigate to
modulename
directory
cd /moduleName
- To compile the Module
sudo make
- To destroy the Module
sudo make clean
General instructions for Modules:
- List All Modules loaded into Kernel
sudo lsmod
same info can be obtained from proc fs
cat /proc/modules
- Filtering a specific module named
moduleName
from those loaded in Kernel
sudo lsmod | grep moduleName.
- Remove a Module named
moduleName
loaded into Kernel
sudo rmmod moduleName
- Reading Messages from Kernel Buffer
sudo dmesg
Kernel Modules live in Kernel space and have separate buffer, namely
kernel ring buffer
where they write their output.
- Clearing Kernel Buffer
sudo dmesg -c
Being limited in size, Kernel Buffer may get filled with custom Module outputs, so it's a good idea to clean it up.
- Reading proc file named
moduleFile
cat /proc/moduleFile
this will be useful when we'll create files in proc file system and read them from user space.
Make your first Kernel Module named simple
, which will essentially just log some messsages when it's loaded and is removed.
We'll also explore some of Kernel headers and utilize them in the module.
Head over to simple
Next, Module hello
will be used to create a proc file named hello
, which will send us 'Hello World' from Kernel space
Head over to hello
Jiffies
is a global variable defined in Linux header linux/jiffies.h
, it represents the number of ticks of system clock from system boot till the time it's value is read.
We'll display it's value in a proc file
Head over to jiffies
Dynamically calculate the time duration passed since a module is loaded, and display the value in seconds again in proc file named seconds
.
Along with jiffies, we'll utilize another constant variable named HZ
, which represents the tick frequency, to calculate time elapsed since module was loaded.
Head over to seconds
We just touched upon the surface of what Linux Kernel modules actually are, there's so much more to learn!
Dive Deeper into Kernel and Kernel Modules with The Linux Kernel Module Programming Guide
- This Project statement,steps and source codes:
- The Linux Operating System source code:
- Understanding Linux Kernel and Makefile :
- Linux Terminal Commands: