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、内核调试的第一块基石

相关推荐
Coder个人博客2 小时前
Linux6.19-ARM64 mm proc子模块深入分析
linux·安全·车载系统·系统架构·系统安全·鸿蒙系统·安全架构
学嵌入式的小杨同学2 小时前
【嵌入式 Linux 实战 1】Ubuntu 环境搭建 + 目录结构详解:嵌入式开发入门第一步
linux·c语言·开发语言·数据结构·vscode·vim·unix
optimistic_chen2 小时前
【Redis系列】分布式锁
linux·数据库·redis·分布式·缓存
xiaoliuliu123452 小时前
openssl-libs-1.1.1f-4.p12.ky10.x86_64.rpm 安装指南 解决依赖与常见报错
linux
重生之绝世牛码2 小时前
Linux软件安装 —— PostgreSQL集群安装(主从复制集群)
大数据·linux·运维·数据库·postgresql·软件安装·postgresql主从集群
17(无规则自律)2 小时前
【CSAPP 读书笔记】第一章:计算机系统漫游
linux·c语言·arm开发·嵌入式硬件·学习·ubuntu
天才奇男子3 小时前
LVS原理及部署
linux·运维·云原生·wpf·lvs·linux chrony
梁洪飞3 小时前
内核启动卡死在Starting kernel ...,没有任何打印如何定位
linux·arm开发·嵌入式硬件·arm
321.。3 小时前
深入理解 Linux 线程封装:从 pthread 到 C++ 面向对象实现
linux·开发语言·c++