内核模块编写
好,我明白了,你是要一个实验报告式的答案:
- 一开始就把全部需要的文件和最终完整代码给出来。
- 后续每一步只描述目的 → 运行哪个文件 → 操作命令 → 验证命令 → 预期输出,不重复贴代码。
这样报告既精简,又符合实验报告的层次性。
一、实验文件说明
本实验一共需要 2 个文件:
- mychardev.c — 内核模块 C 源码(最终版本,已经包含全部功能)
- Makefile — 编译内核模块的构建文件
mychardev.c(最终版)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#define DEVICE_NAME "mychardev"
#define BUF_LEN 256
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Char device with optional uppercase output");
static int major_num = 0;
static char device_buffer[BUF_LEN] = {0};
static int cap = 0;
module_param(cap, int, 0644);
MODULE_PARM_DESC(cap, "Set to 1 to convert input to uppercase");
static ssize_t dev_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos)
{
size_t i = 0;
size_t copy_len = (len < BUF_LEN - 1) ? len : (BUF_LEN - 1);
memset(device_buffer, 0, BUF_LEN);
if (copy_from_user(device_buffer, buf, copy_len) != 0) {
printk(KERN_ERR "copy_from_user failed\n");
return -EFAULT;
}
if (cap == 1) {
for (i = 0; i < copy_len; i++) {
if (device_buffer[i] >= 'a' && device_buffer[i] <= 'z') {
device_buffer[i] -= 32;
}
}
}
printk(KERN_INFO "Output: %s\n", device_buffer);
return copy_len;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.write = dev_write
};
static int __init mychardev_init(void)
{
major_num = register_chrdev(0, DEVICE_NAME, &fops);
if (major_num < 0) {
printk(KERN_ERR "Failed to register char device\n");
return major_num;
}
printk(KERN_INFO "Module loaded, major = %d, cap=%d\n", major_num, cap);
printk(KERN_INFO "Create device: mknod /dev/%s c %d 0\n", DEVICE_NAME, major_num);
return 0;
}
static void __exit mychardev_exit(void)
{
unregister_chrdev(major_num, DEVICE_NAME);
printk(KERN_INFO "Module unloaded\n");
}
module_init(mychardev_init);
module_exit(mychardev_exit);
Makefile
obj-m += mychardev.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
二、分阶段实验步骤
注意:每个阶段都运行 同一个 mychardev.c 文件(最终版),只是关注不同功能是否正常。
阶段 1:模块框架验证
目的
验证模块能正常编译、加载、卸载,并在 dmesg 输出信息。
运行文件
mychardev.c(最终版)
操作命令
make
sudo insmod mychardev.ko
验证成功的命令
dmesg | tail -n 5
预期输出
[xxxx.xx] Module loaded, major = <num>, cap=0
[xxxx.xx] Create device: mknod /dev/mychardev c <num> 0
卸载:
sudo rmmod mychardev
dmesg | tail -n 5
预期:
[xxxx.xx] Module unloaded
阶段 2:注册字符设备验证
目的
确认 major number 正常分配,能够手动创建设备文件。
运行文件
mychardev.c(最终版)
操作命令
sudo insmod mychardev.ko
sudo mknod /dev/mychardev c <major> 0
ls -l /dev/mychardev
验证成功的命令
ls -l /dev/mychardev
预期输出
crw-r--r-- 1 root root 240, 0 Aug 8 12:00 /dev/mychardev
卸载并删除设备文件:
sudo rmmod mychardev
sudo rm /dev/mychardev
阶段 3:write 功能验证
目的
验证能向 /dev/mychardev 写入数据并在 dmesg 打印。
运行文件
mychardev.c(最终版)
操作命令
sudo insmod mychardev.ko
sudo mknod /dev/mychardev c <major> 0
echo "hello kernel" > /dev/mychardev
验证成功的命令
dmesg | tail -n 5
预期输出
[xxxx.xx] Output: hello kernel
阶段 4:cap 参数验证
目的
验证加载模块时设置 cap=1 能将输出转为大写。
运行文件
mychardev.c(最终版)
操作命令
sudo insmod mychardev.ko cap=1
sudo mknod /dev/mychardev c <major> 0
echo "hello kernel" > /dev/mychardev
验证成功的命令
dmesg | tail -n 5
预期输出
[xxxx.xx] Output: HELLO KERNEL
卸载并删除设备文件:
sudo rmmod mychardev
sudo rm /dev/mychardev
这样写的实验报告好处是:
- 一开始就给出完整文件和代码,后面不用重复贴代码。
- 每一步的目标、运行文件、命令和预期结果都清晰独立,方便截图验证。
我可以帮你把这份内容直接排版成实验报告模板格式(带表格和截图占位符),这样你直接填截图就能交作业。
你要我帮你做这个实验报告模板吗?这样可以直接生成可提交的文档结构。