关于 linux-kernel-modules
指导 Agent 编写和维护可加载 Linux 内核模块,面向内核开发者及驱动编写者。内容从 Kbuild、模块接口和字符设备延伸到调试、测试与 Secure Boot 签名。
主要功能
- 01使用 Kbuild 构建模块
- 02配置模块参数与 /proc、sysfs 接口
- 03实现字符设备
- 04使用 KGDB 和 ftrace 调试
- 05KUnit 模块测试
- 06Secure Boot 模块签名
适用场景
- 01创建并加载 Linux 内核模块
- 02向模块添加参数或 sysfs 接口
- 03实现字符设备驱动
- 04调试、测试或签署内核模块
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill linux-kernel-modules安装后在 Claude Code 中输入/linux-kernel-modules确认已加载。
附带文件references/kbuild-basics.md
详细介绍
Linux 内核模块
用途
指导 Agent 编写可加载的 Linux 内核模块(LKM):Kbuild 构建系统、模块参数、/proc 和 sysfs 接口、字符设备实现、使用 KGDB 和 ftrace 调试内核,以及为 Secure Boot 签署模块。
触发条件
- “如何编写 Linux 内核模块?”
- “如何为内核模块添加参数?”
- “如何创建 /proc 或 sysfs 条目?”
- “如何实现字符设备驱动?”
- “如何使用 KGDB 调试内核模块?”
- “如何为 Secure Boot 签署内核模块?”
工作流
1. 最简内核模块
// hello.c — minimal loadable kernel module
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Minimal hello world module");
MODULE_VERSION("1.0");
static int __init hello_init(void)
{
printk(KERN_INFO "hello: module loaded\n");
return 0; // non-zero = load failure
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "hello: module unloaded\n");
}
module_init(hello_init);
module_exit(hello_exit);
# Makefile — must be exactly this structure for Kbuild
obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
# Build
make
# Load
sudo insmod hello.ko
# Check it loaded
lsmod | grep hello
dmesg | tail -5 # see printk output
# Unload
sudo rmmod hello
# Show module info
modinfo hello.ko
2. 模块参数
#include <linux/moduleparam.h>
static int count = 1;
static char *name = "world";
// module_param(variable, type, permissions)
// permissions: 0 = no sysfs entry, S_IRUGO = readable, S_IWUSR = writable
module_param(count, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(count, "Number of times to print (default: 1)");
module_param(name, charp, S_IRUGO);
MODULE_PARM_DESC(name, "Name to greet (default: world)");
static int __init hello_init(void)
{
int i;
for (i = 0; i < count; i++)
printk(KERN_INFO "hello: Hello, %s!\n", name);
return 0;
}
# Pass parameters at load time
sudo insmod hello.ko count=3 name="kernel"
# Modify at runtime (if S_IWUSR set)
echo 5 > /sys/module/hello/parameters/count
3. /proc 文件系统接口
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
static struct proc_dir_entry *proc_entry;
static int mymod_show(struct seq_file *m, void *v)
{
seq_printf(m, "Counter: %d\n", my_counter);
seq_printf(m, "Status: %s\n", my_status ? "active" : "idle");
return 0;
}
static int mymod_open(struct inode *inode, struct file *file)
{
return single_open(file, mymod_show, NULL);
}
static const struct proc_ops mymod_fops = {
.proc_open = mymod_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static int __init mymod_init(void)
{
proc_entry = proc_create("mymod", 0444, NULL, &mymod_fops);
if (!proc_entry)
return -ENOMEM;
return 0;
}
static void __exit mymod_exit(void)
{
proc_remove(proc_entry);
}
cat /proc/mymod
4. sysfs 接口
#include <linux/kobject.h>
#include <linux/sysfs.h>
static struct kobject *mymod_kobj;
static int mymod_value = 42;
static ssize_t value_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", mymod_value);
}
static ssize_t value_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
sscanf(buf, "%d", &mymod_value);
return count;
}
static struct kobj_attribute value_attr =
__ATTR(value, 0664, value_show, value_store);
static int __init mymod_init(void)
{
mymod_kobj = kobject_create_and_add("mymod", kernel_kobj);
if (!mymod_kobj) return -ENOMEM;
return sysfs_create_file(mymod_kobj, &value_attr.attr);
}
static void __exit mymod_exit(void)
{
sysfs_remove_file(mymod_kobj, &value_attr.attr);
kobject_put(mymod_kobj);
}
cat /sys/kernel/mymod/value
echo 100 > /sys/kernel/mymod/value
5. 字符设备
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "mydev"
#define BUF_SIZE 1024
static int major;
static struct cdev my_cdev;
static char kernel_buf[BUF_SIZE];
static int mydev_open(struct inode *inode, struct file *file) { return 0; }
static int mydev_release(struct inode *inode, struct file *file) { return 0; }
static ssize_t mydev_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{
size_t to_copy = min(len, (size_t)BUF_SIZE);
if (copy_to_user(buf, kernel_buf, to_copy)) return -EFAULT;
return to_copy;
}
static ssize_t mydev_write(struct file *f, const char __user *buf, size_t len, loff_t *off)
{
size_t to_copy = min(len, (size_t)(BUF_SIZE - 1));
if (copy_from_user(kernel_buf, buf, to_copy)) return -EFAULT;
kernel_buf[to_copy] = '\0';
return to_copy;
}
static const struct file_operations mydev_fops = {
.owner = THIS_MODULE,
.open = mydev_open,
.release = mydev_release,
.read = mydev_read,
.write = mydev_write,
};
static int __init mydev_init(void)
{
major = register_chrdev(0, DEVICE_NAME, &mydev_fops);
if (major < 0) return major;
printk(KERN_INFO "mydev: registered with major %d\n", major);
return 0;
}
# Create device node (after loading module)
sudo mknod /dev/mydev c $(cat /proc/devices | grep mydev | awk '{print $1}') 0
echo "test" > /dev/mydev
cat /dev/mydev
6. 使用 KGDB 和 ftrace 调试
# KGDB — kernel GDB via serial/network
# Boot with: kgdboc=ttyS0,115200 kgdbwait
# Or over network: [email protected]/,@192.168.1.11/
# On debug host:
gdb vmlinux
(gdb) target remote /dev/ttyS0
(gdb) set architecture i386:x86-64:intel
(gdb) info registers
# ftrace — kernel function tracer
echo function > /sys/kernel/debug/tracing/current_tracer
echo mymod_write > /sys/kernel/debug/tracing/set_ftrace_filter
echo 1 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace
# Dynamic debug — enable pr_debug() output
echo "module hello +p" > /sys/kernel/debug/dynamic_debug/control
7. EXPORT_SYMBOL 与 EXPORT_SYMBOL_GPL
// EXPORT_SYMBOL — any module can link (including proprietary)
EXPORT_SYMBOL(my_helper);
// EXPORT_SYMBOL_GPL — only GPL-compatible modules can link
EXPORT_SYMBOL_GPL(gpl_only_fn);
对于涉及 GPL 专有内核内部实现的符号,请使用 EXPORT_SYMBOL_GPL。导入 GPL 符号的非 GPL 模块在加载时会失败,并显示 taint 警告。执行 insmod 后,使用 modinfo 和 dmesg 检查。
8. 内核中的 KUnit 单元测试
// test_mymod.c — compile into module or standalone KUnit module
#include <kunit/test.h>
#include "mymod_internal.h" // functions under test
static void test_parse_valid(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, mymod_parse("42"), 42);
}
static struct kunit_case mymod_cases[] = {
KUNIT_CASE(test_parse_valid),
{}
};
static struct kunit_suite mymod_suite = {
.name = "mymod",
.test_cases = mymod_cases,
};
kunit_test_suite(mymod_suite);
# In-tree KUnit run (kernel tree with CONFIG_KUNIT=y)
./tools/testing/kunit/kunit.py run --filter mymod
# Out-of-tree: enable CONFIG_KUNIT in test kernel or use kunit module target
完整的 KUnit、kselftest 和 syzkaller 工作流,请参阅 skills/kernel/kernel-testing。
9. Rust 内核模块(kernel 6.x + CONFIG_RUST=y)
需要使用 CONFIG_RUST=y 构建内核,并使用由内核源码树指定版本的 Rust 工具链。
// drivers/rust_example/lib.rs
use kernel::prelude::*;
module! {
type: RustExample,
name: "rust_example",
author: "You",
description: "Rust LKM example",
license: "GPL",
}
struct RustExample;
impl kernel::Module for RustExample {
fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust kernel module loaded\n");
Ok(RustExample)
}
}
# Build from kernel tree with Rust support enabled
make LLVM=1 rustavailable # verify Rust toolchain
make M=drivers/rust_example modules
sudo insmod drivers/rust_example/rust_example.ko
10. 模块签名(Secure Boot,kernel 6.x)
启用 CONFIG_MODULE_SIG 后,kernel 6.x 会强制验证模块签名(大多数启用了 Secure Boot 的发行版内核默认启用此选项)。
# Generate signing key (use org PKI in production)
openssl req -new -x509 -newkey rsa:4096 \
-keyout signing_key.pem -out signing_cert.pem \
-days 3650 -subj "/CN=Kernel Module Signing/" -nodes
# Sign module (sha256 or sha512 per kernel config)
/usr/src/linux-headers-$(uname -r)/scripts/sign-file \
sha256 signing_key.pem signing_cert.pem hello.ko
# Verify signature embedded in .ko
modinfo hello.ko | grep signer
# Enroll key for Secure Boot (MOK on UEFI)
sudo mokutil --import signing_cert.pem
# Reboot → MOK Manager → enroll → reboot again
# Kernel lockdown mode check
cat /sys/kernel/security/lockdown
启用 lockdown integrity 或 confidentiality 时,会拒绝未签名的模块。发行版内核也可能要求密钥已加入内核内置的受信任密钥环(CONFIG_SYSTEM_TRUSTED_KEYS)。
有关 Kbuild 系统的详细信息,请参阅 references/kbuild-basics.md。
相关技能
- 使用
skills/kernel/kernel-testing获取 KUnit 和 kselftest 测试框架 - 使用
skills/kernel/device-drivers获取超出最简模块范畴的字符设备/平台驱动模式 - 使用
skills/kernel/kernel-debugging获取 kgdb、ftrace 和 kprobes 相关内容 - 使用
skills/observability/ebpf在用户空间跟踪内核,而无需使用模块 - 使用
skills/debuggers/gdb通过 KGDB 管理 GDB 会话 - 使用
skills/binaries/elf-inspection检查模块 ELF 结构