源代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/slab.h>
// 定义设备名称
#define DEVICE_NAME "mychardev"
static int major; // 保存设备的主设备号
static struct cdev my_cdev; // 字符设备结构
static int cap = 0; // 模块参数,控制是否转换为大写
module_param(cap, int, S_IRUGO); // 声明模块参数
// 写操作函数
static ssize_t mychardev_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
char *kernel_buf;
int i;
// 为内核缓冲区分配内存
kernel_buf = kmalloc(count + 1, GFP_KERNEL);
if (!kernel_buf)
return -ENOMEM; // 内存分配失败
// 从用户空间复制数据到内核空间
if (copy_from_user(kernel_buf, buf, count)) {
kfree(kernel_buf);
return -EFAULT; // 复制失败
}
kernel_buf[count] = '\0'; // 确保字符串以空字符结尾
// 如果 cap 参数为 1,将输入转换为大写
if (cap == 1) {
for (i = 0; i < count; i++) {
if (kernel_buf[i] >= 'a' && kernel_buf[i] <= 'z') {
kernel_buf[i] -= 32; // 小写转大写
}
}
}
// 打印到内核日志
printk(KERN_INFO "mychardev: %s\n", kernel_buf);
kfree(kernel_buf); // 释放内存
return count; // 返回写入的字节数
}
// 文件操作结构体
static struct file_operations fops = {
.owner = THIS_MODULE,
.write = mychardev_write,
};
// 模块加载函数
static int __init mychardev_init(void) {
dev_t dev;
// 分配字符设备号
if (alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME) < 0) {
return -1; // 分配失败
}
major = MAJOR(dev); // 获取主设备号
cdev_init(&my_cdev, &fops); // 初始化字符设备
my_cdev.owner = THIS_MODULE;
// 添加字符设备到系统
if (cdev_add(&my_cdev, dev, 1) == -1) {
unregister_chrdev_region(dev, 1); // 失败时释放设备号
return -1;
}
printk(KERN_INFO "mychardev: Registered with major number %d\n", major);
return 0;
}
// 模块卸载函数
static void __exit mychardev_exit(void) {
cdev_del(&my_cdev); // 删除字符设备
unregister_chrdev_region(MKDEV(major, 0), 1); // 释放设备号
printk(KERN_INFO "mychardev: Unregistered\n");
}
module_init(mychardev_init); // 指定模块加载函数
module_exit(mychardev_exit); // 指定模块卸载函数
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pei Youjin");
MODULE_DESCRIPTION("A simple character device module");
编译和加载模块
-
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
-
编译
make
-
加载模块
sudo insmod mychardev.ko cap=1
-
创建设备节点
sudo mknod /dev/mychardev c [major_number] 0
用
dmesg
命令查看major_number
。 -
测试写入
echo "hello world" > /dev/mychardev
-
卸载模块
sudo rmmod mychardev
可能出现的问题
如果内核的构建目录不存在或文件丢失,可以尝试以下步骤:
-
安装内核头文件:
- 使用包管理器安装内核头文件。对于 Ubuntu,可以运行:
复制
sudo apt update sudo apt install linux-headers-$(uname -r)
- 使用包管理器安装内核头文件。对于 Ubuntu,可以运行:
-
检查符号链接:
- 确保
/lib/modules/$(uname -r)/build
是指向正确的内核头文件目录的符号链接。
- 确保
-
重建内核头文件:
- 如果需要手动构建,可以从内核源代码目录运行以下命令:
复制
make oldconfig make prepare
- 如果需要手动构建,可以从内核源代码目录运行以下命令:
-
下载内核源代码:
- 如果没有内核源代码,可以从 kernel.org 下载,并在本地解压和配置。