Linux增加简单的内核模块

源代码

#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");

编译和加载模块

  1. 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
    
  2. 编译

     
    make
    
  3. 加载模块

    sudo insmod mychardev.ko cap=1
    
  4. 创建设备节点

    sudo mknod /dev/mychardev c [major_number] 0
    

    用 dmesg 命令查看 major_number

  5. 测试写入

    echo "hello world" > /dev/mychardev
    
  6. 卸载模块

    sudo rmmod mychardev

可能出现的问题

如果内核的构建目录不存在或文件丢失,可以尝试以下步骤:

  1. 安装内核头文件

    • 使用包管理器安装内核头文件。对于 Ubuntu,可以运行:

      复制

      sudo apt update
      sudo apt install linux-headers-$(uname -r)
      
  2. 检查符号链接

    • 确保 /lib/modules/$(uname -r)/build 是指向正确的内核头文件目录的符号链接。
  3. 重建内核头文件

    • 如果需要手动构建,可以从内核源代码目录运行以下命令:

      复制

      make oldconfig
      make prepare
      
  4. 下载内核源代码

    • 如果没有内核源代码,可以从 kernel.org 下载,并在本地解压和配置。
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值