openEuler 内核解读(五):Linux 内核模块 “Hello World” 示例

🧪 实验目标

编写一个最简内核模块,在加载时打印 Hello, openEuler!,卸载时打印 Goodbye, openEuler!


一、准备工作(在 openEuler 系统中操作)

1. 安装必要工具

bash 复制代码
sudo dnf install -y kernel-devel kernel-headers gcc make elfutils-libelf-devel

✅ 验证:确保 /lib/modules/$(uname -r)/build 存在(指向内核源码头文件)

2. 创建工作目录

bash 复制代码
mkdir ~/hello_module && cd ~/hello_module

二、编写内核模块代码

创建文件 hello.c

c 复制代码
/* hello.c - 最简内核模块 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World module for openEuler");
MODULE_VERSION("0.1");

// 模块加载时执行
static int __init hello_init(void)
{
    printk(KERN_INFO "Hello, openEuler!\n");
    return 0; // 成功
}

// 模块卸载时执行
static void __exit hello_exit(void)
{
    printk(KERN_INFO "Goodbye, openEuler!\n");
}

// 注册入口和出口函数
module_init(hello_init);
module_exit(hello_exit);

🔍 说明:

  • printk 是内核版的 printf,输出到内核日志(非终端)
  • KERN_INFO 是日志级别(对应 syslog 的 info)
  • __init / __exit 是内存优化标记(初始化后释放)

三、编写 Makefile

创建 Makefile(注意:必须是 Makefile,首字母大写):

makefile 复制代码
# Makefile for hello.ko
obj-m += hello.o

# 获取当前内核构建目录
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	make -C $(KDIR) M=$(PWD) clean

⚠️ 注意:缩进必须用 Tab(不是空格),否则 make 报错。


四、编译模块

bash 复制代码
make

成功后生成:

  • hello.ko(可加载内核模块)
  • hello.mod.c, hello.mod.o(中间文件)

✅ 验证:file hello.ko 应显示 "Linux kernel module"


五、加载与测试

1. 加载模块

bash 复制代码
sudo insmod hello.ko

2. 查看内核日志

bash 复制代码
dmesg | tail -n 2

预期输出:

复制代码
[XXXXX.XXXXXX] Hello, openEuler!

3. 卸载模块

bash 复制代码
sudo rmmod hello

再次查看日志:

bash 复制代码
dmesg | tail -n 2

预期输出:

复制代码
[XXXXX.XXXXXX] Goodbye, openEuler!

4. (可选)查看已加载模块

bash 复制代码
lsmod | grep hello

六、常见问题排查

问题 原因 解决方案
insmod: ERROR: could not insert module hello.ko: Invalid module format 内核版本不匹配 确保 kernel-devel 版本与 uname -r 一致
make: *** /lib/modules/.../build: No such file or directory 未安装 kernel-devel 执行 sudo dnf install kernel-devel-$(uname -r)
无输出 日志级别被过滤 检查 cat /proc/sys/kernel/printk,默认应为 4 4 1 7

七、扩展思考(面向学习者)

  1. 为什么不用 printf

    → 用户态 C 库(glibc)不可用,内核有独立运行环境。

  2. printk 输出去哪了?

    → 写入内核环形缓冲区(ring buffer),由 klogdjournald 转发到 /var/log/messagesdmesg

  3. 能否传递参数?

    → 可以!使用 module_param(),例如:

    c 复制代码
    static char *name = "openEuler";
    module_param(name, charp, S_IRUGO);

    加载时:sudo insmod hello.ko name="World"


八、安全与规范提醒

  • 内核模块拥有 最高权限(ring 0),bug 可能导致系统崩溃(oops/panic)
  • 生产环境禁止随意加载未签名模块(openEuler 默认启用 Secure Boot + Lockdown LSM
  • 开发建议在 虚拟机 中进行

✅ 至此,你已完成第一个 Linux 内核模块开发,这是通往设备驱动、eBPF、内核调试的第一块基石

相关推荐
酉鬼女又兒13 分钟前
零基础入门Linux指南:每天一个Linux命令_sed
linux·运维·服务器
daad77716 分钟前
tcpdump_BPF
linux·测试工具·tcpdump
予枫的编程笔记20 分钟前
【Linux进阶篇】Linux网络配置+端口监听实战:ip/ss/iptables常用命令一次吃透
linux·iptables·网络配置·curl·端口监听·ping·ss命令
礼拜天没时间.27 分钟前
深入Docker架构——C/S模式解析
linux·docker·容器·架构·centos
醉风塘34 分钟前
Linux进程管理:深度解析ps -ef命令及其高级应用
linux·运维·服务器
不做无法实现的梦~36 分钟前
PX4各个模块的作用(3)
linux·stm32·嵌入式硬件·机器人·自动驾驶
不爱缺氧i1 小时前
ubuntu离线安装mariadb
linux·ubuntu·mariadb
疯狂敲代码的老刘1 小时前
JDK 1.6到25 全版本网盘合集 (Windows + Mac + Linux)
java·linux·windows·macos·jdk
爆米花byh1 小时前
在RockyLinux9环境的Storm2.8.3单机版安装
linux·中间件·storm
纤纡.1 小时前
Linux 下 MySQL 数据类型与约束:第三章核心表格归纳与实战应用
linux·mysql