Linux内核模块的编译

Linux内核采用了模块化的设计,允许内核在运行时插入或删除代码。这些代码组合在一起,形成了所谓的模块。

支持模块的好处是内核可以尽可能的小,可选的功能和驱动则利用模块来提供。

Hello, World!

/* hello world module */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

static int __init hello_init(void)  // 初始化函数,模块装载时被调用
{
        printk("<1> Hello, World!\n");
        return 0;
}

static void __exit hello_exit(void) // 退出函数,模块卸载的时候被调用
{
        printk(KERN_ALERT "hello module exit.\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("hello world");
MODULE_DESCRIPTION("first module");

模块的出口和入口都通过调用module_init()来注册到系统中,唯一的参数是模块的初始化函数。它其实是一个宏调用而非函数。 所有初始化函数必须满足如下形式:

int my_init(void);

类似的,退出函数使用module_exit()来注册,对应的函数作为出口函数,负责在返回(具体指了卸载内核模块)之前清理资源。 如果模块被静态地编译进内核,则这部分不会被调用,因为内核不需要卸载操作。

构建内核模块

内核模块的构建需要使用Makefile,使用一下模板可以完成编译:

ifneq ($(KERNELRELEASE),)
    obj-m += main.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := "$(shell pwd)"

default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    rm main.mod.c main.mod.o main.o modules.order Module.symvers .main.ko.cmd main.mod .main.mod.* .main.o.* -rf
endif

另外在Clion中使用如下的CMakeLists.txt,可以消除错误的报错信息并拥有相应的代码提示。在运行时不需要指定可执行目标,直接运行编译即可在根目录编译产生main.ko(即为一个内核模块),使用insmod命令加载即可。

cmake_minimum_required(VERSION 3.15)
project(default)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -nostdinc")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -nostdinc")

set(KERNEL_ROOT /usr/src/linux)
include_directories(
        # kernel headers
        "${KERNEL_ROOT}/include"
        "${KERNEL_ROOT}/arch/arm64/include"
        "${KERNEL_ROOT}/arch/arm/include"
        "${KERNEL_ROOT}/mm"
        "/usr/include"
)
add_definitions(-D__KERNEL__=1)

add_custom_target(default_custom COMMAND make -C "${default_SOURCE_DIR}"
        CLION_EXE_DIR="${PROJECT_BINARY_DIR}")

Last updated

Was this helpful?