mcpField
L
linux-kernel-modules

linux-kernel-modules

github.com/mohitmishra786开发269649 次安装18 周前更新
代码仓库

关于 linux-kernel-modules

指导 Agent 编写和维护可加载 Linux 内核模块,面向内核开发者及驱动编写者。内容从 Kbuild、模块接口和字符设备延伸到调试、测试与 Secure Boot 签名。

主要功能

  1. 01使用 Kbuild 构建模块
  2. 02配置模块参数与 /proc、sysfs 接口
  3. 03实现字符设备
  4. 04使用 KGDB 和 ftrace 调试
  5. 05KUnit 模块测试
  6. 06Secure Boot 模块签名

适用场景

  1. 01创建并加载 Linux 内核模块
  2. 02向模块添加参数或 sysfs 接口
  3. 03实现字符设备驱动
  4. 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 结构

评论

支持 Markdown