# Linux Kernel Labs: Kernel Module and Kernel  API    

Ok, I've started a new course on the Linux kernel. It's mostly labs and assignments with lectures.

The course [Operating System 2](https://linux-kernel-labs.github.io/refs/heads/master/so2/index.html#operating-systems-2) or [Linux Kernel Labs](https://linux-kernel-labs.github.io/refs/heads/master/so2/index.html#operating-systems-2) is focused on the Linux kernel, on how it works and how to interact with it. It explores major concepts such as processes, filesystems, I/O, and more. Most of the labs and assignments involve building kernel drivers, giving us real hands-on practice by loading them into a running kernel.  
You can go on the website to get more informations about it:

*   Linux Kernel version: 5.10.14
    
*   Labs: To discover concepts
    
*   Assignments
    
*   Machine configuration/Setup: [https://gitlab.cs.pub.ro/so2](https://gitlab.cs.pub.ro/so2)
    

In this serie I'll talk mostly about the labs and assignments

# Lectures

The lectures cover system calls, processes, and interrupts.

It covers how system calls are bound to a number, processes with their virtual memory and the `task_struct` that represents a process in the Linux kernel, and other topics.

Let's move on to the labs (they were more interesting and less boring, sorry 😅). And if you took a course on [operating systems](https://pdos.csail.mit.edu/6.1810/2025/index.html) like the one offered by MIT, there is some common knowledge and patterns you'll quickly notice.  
You'll feel much less overwhelmed by all the concepts of the Linux kernel.

# LABS

## Kernel Modules

[Link](https://linux-kernel-labs.github.io/refs/heads/master/labs/kernel_modules.html)

The tasks in this lab are about creating, loading, and unloading kernel modules.

Kernel modules (`.ko` files) are compiled source code that can be loaded into a running kernel, so there is no need to stop or restart the OS. They run at the kernel level. Inside kernel modules, we can use the kernel API to do many things like getting information about processes, changing their behavior, allocating memory, and much more.  
You write them like C code (with some conventions), write a Kbuild file, and that's it.

What's Kbuild ?

> **Kbuild** is the primary build system used by the Linux Kernel . It compiles both the core kernel and loadable modules, handling complex dependencies, compiler flags, and architecture-specific configurations across various hardware platforms.
> 
> `Google`.

Here is an example of a kernel module

`Kbuild`

```shell
obj-m = hello_mod.o
```

`hello_module.c`

```c
#include <linux/module.h>// always required
// export functions like module_init/module_exit
 
#include <linux/init.h>
 
#include <linux/kernel.h> // export basic kernel macros 
// like pr_info/printk, pr_debug

MODULE_DESCRIPTION("Simple module");
MODULE_AUTHOR("Kernel Hacker");
MODULE_LICENSE("GPL"); // Module license and code that can be used. For example: "GPL" unlocks GPL-only kernel symbols.

static int my_hello_init(void)
{
	pr_debug("Hello!\n");
	return 0;
}

static void hello_exit(void)
{
	pr_debug("Goodbye!\n");
}

module_init(my_hello_init); // Will invoke my_hello_init after loading the module

module_exit(hello_exit); // Will invoke hello_exit after unloading 
```

And with a command like the following:

```shell
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
```

where

*   `-C /lib/modules/$(uname -r)/build`: kernel build system directory. Points to headers needed to build modules against the running kernel.
    
*   `M=$(pwd)`: your module directory (where your Kbuild sits)
    
*   `modules`: target (we're building a module)
    

So after that we'll have a `hello_mod.ko`

Then:

*   load it into the kernel: `insmod hello_mod.ko` (need root privileges)
    
*   unload it from the kernel: `rmmod hello_mod` (.ko is not required when unloading)
    
*   to list loaded modules: `lsmod`
    

Something to remember, debugging in the kernel cannot be done like in our usual C code with a `printf`, these functions are not available at the kernel level. But we can read the debug info from the kernel log by using `dmesg` (display message).  
You can read more about it on the course, internet and play with it.

## Kernel API

[Link](https://linux-kernel-labs.github.io/refs/heads/master/labs/kernel_api.html)

The tasks here are about some Kernel API to interact with...the kernel:

*   Memory allocation: `kmalloc`
    
*   Locking with **mutex** like `mutex_lock` , **spinlock** like `spin_lock` or `read_lock` (for locking when reading) and many more
    
*   Atomic methods like `atomic_read` to read `atomic_t` variables safely with no race condition
    
*   List: doubly linked list (yes the kernel has a dedicated list data structure)
    

It's the list api that I really like. Why ? The way to use it: a little ambiguous but meaningful.

## List API

The list data structure of the kernel is a doubly linked list defined in [include/linux/types.h](https://elixir.bootlin.com/linux/v4.19.19/source/include/linux/types.h) and its methods are in [include/linux/list.h](https://elixir.bootlin.com/linux/v4.19.19/source/include/linux/list.h) .

What's interesting is that it's an independent structure that we can add as an attribute to our own structure to have all the capabilities it offers.  
Let's assume you have a structure like

`my_data`

```c
struct my_data {
    u8 access;
    char name[25];
    int num;
};

// and use it like
struct my_data data1;
struct my_data data2;
...
```

And you want to have a list of `my_data`, you just need to:

*   add the `struct list_head` as an attribute to `my_data`
    
*   Set a head that will be used to access your list
    

```c
struct my_data {
    u8 access;
    char name[25];
    int num;

    struct list_head my_list; // (1)
};

LIST_HEAD(my_head); // (2)
```

Easy.

So as `list_head` has `prev` and `next` as attributes which are respectively pointers to the previous and next data (in our case `data1`, `data2`), we can easily access them through `my_list`.

But did you notice that there is no link between `my_list` and `head` 😅 ? and also the way we define the head ? 😅 . A little funny and confusing.

Yes at this point, adding `list_head` to `my_data` is like

> we'll have a list of my\_data and `my_list` attribute will be used as the corresponding node of the target data in the list

And by doing `LIST_HEAD(my_head)`, `LIST_HEAD` is a macro that takes a name (here `my_head`) and internally sets it as a list head, so it's like

> my\_head will be the head of a list (of type struct list\_head of course) and set its prev to itself

So at this point, no link between the declarations.

To now connect both or start using your list, you need to use the provided methods that work on the list: we'll see just 3 (there are plenty of them). Some of them are macros, others are regular functions.

### Add element: list\_add

To add an element to the list we use

```c
list_add(struct list_head *new, struct list_head *head)
```

so something like

```c
list_add(&data1.my_list, &head);
```

### Access element: list\_entry, list\_for\_each

we can use the provided macro

```c
list_for_each(pos, head);
```

that iterates over the list where:

*   pos: is a pointer to a **struct list\_head** used as a cursor to allow stepping from one element of the node to another
    
*   head: is a pointer to the head
    

Internally it's the usual loop we write to iterate over a list, here is the code of this macro in `include/linux/list.h`

```c
/**
 * list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 */

#define list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)
```

Then inside this macro we'll use

```c
list_entry(ptr, type, member); // return a pointer to struct my_data
```

That returns the entry at each iteration.  
where

*   ptr: is the cursor defined for `list_for_each`
    
*   type: literally the type of struct (`struct my_data`)
    
*   member: the list\_head attribute added to the struct
    

So it's used like this

```c
struct list_head *cursor;
struct my_data *temp;

list_for_each(cursor, &head) {
	temp = list_entry(cursor, struct my_data, my_list);
	pr_info("(%d, %s) ", temp->access, ti->name);
}
```

It can be a little confusing but by doing the various labs you can start to feel it.

There are more macros to interact with the list that you can find in `include/linux/list.h`

## Assignments 0: Kernel API

[Link](https://linux-kernel-labs.github.io/refs/heads/master/so2/assign0-kernel-api.html)

[Github Link to my solution](https://github.com/tawaliou/kernel-labs-OS-2/blob/main/0-list/list.c).

The assignment is about the use of the Kernel API. Here's a short version of the tasks:

*   Create a `list.ko` kernel module that uses the Linux kernel list API to store strings.
    
*   Expose a `/proc/list/management` write-only interface to process `addf`, `adde`, `delf`, and `dela` commands like `echo "addf name" > /proc/list/management`).
    
*   Allow reading the list contents line-by-line through the read-only `/proc/list/preview` file through `cat /proc/list/preview`
    
*   A skeleton is already provided for the setup of `preview` and `management` proc files. This part of the code is worth reading.
    
*   A test [`check/_checker`](https://github.com/tawaliou/kernel-labs-OS-2/blob/main/0-list/checker/_checker) is also provided so we can check if our implementation respects the constraints.
    

The work is to parse the write commands and implement for each of the four of them a function to handle the operation. In my case I implemented:

*   `list_proc_addf` for `addf`: [Github Link](https://github.com/tawaliou/kernel-labs-OS-2/blob/05c871ec3b0a6ae1b0546f91f84ccc8fd09209d4/0-list/list.c#L57)
    
*   `list_proc_delf` for `delf`: [Github Link](https://github.com/tawaliou/kernel-labs-OS-2/blob/05c871ec3b0a6ae1b0546f91f84ccc8fd09209d4/0-list/list.c#L79)
    
*   and do the same for the remaining 2 commands
    
*   Then the function to allocate memory for the string and set its value: [Github Link](https://github.com/tawaliou/kernel-labs-OS-2/blob/05c871ec3b0a6ae1b0546f91f84ccc8fd09209d4/0-list/list.c#L43)
    

If you do the lab on kernel api, it'll be easy.

Here is

Few things we need to keep in mind or understand from this assignment:

*   Don't forget to purge (free) the memory you allocate in the module before the exit. In my case I implemented [`list_proc_purge`](https://github.com/tawaliou/kernel-labs-OS-2/blob/05c871ec3b0a6ae1b0546f91f84ccc8fd09209d4/0-list/list.c#L124) that I called inside the [`list_exit`](https://github.com/tawaliou/kernel-labs-OS-2/blob/05c871ec3b0a6ae1b0546f91f84ccc8fd09209d4/0-list/list.c#L223) which is used as the module exit entry point [`module_exit`](https://github.com/tawaliou/kernel-labs-OS-2/blob/05c871ec3b0a6ae1b0546f91f84ccc8fd09209d4/0-list/list.c#L230)(when we remove the module with rmmod). Otherwise there will be a **memory leak in kernel memory.**
    
*   Don't forget to use `copy_from_user` and `copy_to_user` that respectively copy data (buffer) from user space to kernel space and copy data from kernel space to user space (for security reasons...)
    
*   We can use `seq_*` methods like `seq_puts`, `seq_printf` and many more to output data in a `/proc` file. In our case by doing `cat /proc/list/preview`. These functions are better for interacting with `/proc` than `printk` which outputs data in the kernel log (`dmesg`)
    
*   Look at [`include/linux/proc_fs.h`](https://elixir.bootlin.com/linux/v4.19.19/source/include/linux/proc_fs.h) and [fs/proc/generic.c](https://elixir.bootlin.com/linux/v4.19.19/source/fs/proc/generic.c) to have a list of functions to interact with `/proc`, like creating a directory, sub files...
    

## **Next weeks**

The next weeks are about character device drivers, which are kernel modules that allow access to hardware devices, device files, I/O access, interrupts.

By the way, here's my GitHub repo for the assignments: [**kernel-labs-OS-2**](https://github.com/tawaliou/kernel-labs-OS-2)

![](https://cdn.hashnode.com/uploads/covers/5f9bb8f3ff730d5cb8dedb03/949d3175-6b16-47d1-947f-19a5c7ca5a0c.png align="center")

Figure 1: Kernel API assignment tests passed.

![](https://cdn.hashnode.com/uploads/covers/5f9bb8f3ff730d5cb8dedb03/2ff4b09f-415d-4845-a31d-bb7f81ec3edf.png align="center")

Figure 2: `addf` and `adde` commands and `/proc/list/preview` showcase

![](https://cdn.hashnode.com/uploads/covers/5f9bb8f3ff730d5cb8dedb03/da88afee-2b7b-4b26-91d9-d2c3dc980c10.png align="center")

Figure 3: `delf` and `dela` showcase
