当前位置:首页>Linux>Linux驱动编程字符驱动-0x00

Linux驱动编程字符驱动-0x00

  • 2026-09-02 20:13:23
Linux驱动编程字符驱动-0x00

一、核心概念

1.设备号:Linux中每个设备文件(例如/dev/xxx)都对应一个设备号,格式是主设备号 + 次设备号:

  1)主设备号:驱动程序的 "唯一标识",内核通过主设备号,直接找到该

  设备对应的驱动程序(file_operations 结构体);

  2)次设备号:同一驱动下的 "设备编号",区分同一驱动管理的多个设备。

2.file_operations结构体:驱动程序和Linux内核虚拟文件系统(VFS)的 “接口协议”,用户空间调用open/read/write时,内核会通过这个结构体找到驱动里对应的函数;

3.设备节点:也叫设备文件,路径通常是/dev/xxx,是Linux为了让用户空间程序能通过标准文件操作(open/read/write/close)访问硬件设备而设计的特殊文件,它不存储任何数据,只在文件系统的索引节点(inode)中记录了设备类型(字符/块)和对应的主/次设备号,是用户空间与内核驱动、硬件之间的 “桥梁”;

二、设备号的分配方式

1.静态分配(register_chrdev_region)

用法:手动指定主设备号 + 次设备号范围;

dev_t devno = MKDEV(237, 0); // 组合主设备号237、次设备号0

register_chrdev_region(devno, 3, "mychardev"); // 申请次0~2,共3个设备

2.动态分配(alloc_chrdev_region)

alloc_chrdev_region(&devno, 0, 3, "mychardev"); // 次0~2,主设备号由内核分配

//分配后可拆分主/次设备号

int major = MAJOR(devno); // 获取主设备号

int minor = MINOR(devno); // 获取次设备号

三、测试

1.源代码

mychardrv.c

#include<linux/module.h>#include<linux/kernel.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/uaccess.h>#include<linux/slab.h>#define DEVICE_NAME "mychardev"// 设备名称#define DEVICE_SIZE  1024            // 设备虚拟内存大小static struct cdev my_cdev;static dev_t dev_num;static char *device_memory;// 打开设备staticintmy_open(struct inode *inode, struct file *filp){    printk(KERN_INFO "Device opened\n");    filp->private_data = device_memory;    return 0;}// 释放设备staticintmy_release(struct inode *inode, struct file *filp){    printk(KERN_INFO "Device released\n");    return 0;}// 从设备读取数据staticssize_tmy_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){    size_t bytes_read = 0;    char *data = filp->private_data;    if (count > DEVICE_SIZE - *f_pos) {        count = DEVICE_SIZE - *f_pos;    }    if (copy_to_user(buf, data + *f_pos, count)) {        return -EFAULT;    }    bytes_read = count;    *f_pos += bytes_read;    return bytes_read;}// 向设备写入数据staticssize_tmy_write(struct file *filp, constchar __user *buf, size_t count, loff_t *f_pos){    size_t bytes_written = 0;    char *data = filp->private_data;    if (count > DEVICE_SIZE - *f_pos) {        count = DEVICE_SIZE - *f_pos;    }    if (copy_from_user(data + *f_pos, buf, count)) {        return -EFAULT;    }    bytes_written = count;    *f_pos += bytes_written;    return bytes_written;}// 文件操作结构体static struct file_operations my_fops = {   .owner   = THIS_MODULE,   .open    = my_open,   .release = my_release,   .read    = my_read,   .write   = my_write,};// 驱动模块初始化函数staticint __init my_driver_init(void){    int ret;    // 分配设备号 动态分配 1 个字符设备编号,次设备号从 0 开始    ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);    if (ret < 0) {        printk(KERN_ERR "Failed to allocate device number\n");        return ret;    }    printk(KERN_INFO "Allocated character device region: major=%d, minor=%d\n", MAJOR(dev_num), MINOR(dev_num));    // 初始化字符设备    cdev_init(&my_cdev, &my_fops);    my_cdev.owner = THIS_MODULE;    // 添加字符设备到系统    ret = cdev_add(&my_cdev, dev_num, 1);    if (ret < 0) {        printk(KERN_ERR "Failed to add cdev\n");        unregister_chrdev_region(dev_num, 1);        return ret;    }    // 分配设备内存    device_memory = kmalloc(DEVICE_SIZE, GFP_KERNEL);    if (!device_memory) {        printk(KERN_ERR "Failed to allocate device memory\n");        cdev_del(&my_cdev);        unregister_chrdev_region(dev_num, 1);        return -ENOMEM;    }    printk(KERN_INFO "My character device driver loaded successfully\n");    return 0;}// 驱动模块退出函数staticvoid __exit my_driver_exit(void){    kfree(device_memory);    cdev_del(&my_cdev);    unregister_chrdev_region(dev_num, 1);    printk(KERN_INFO "My character device driver unloaded\n");}module_init(my_driver_init);module_exit(my_driver_exit);MODULE_AUTHOR("axun");MODULE_DESCRIPTION("A simple character device driver");MODULE_LICENSE("GPL");

2.Mafile及编译加载驱动模块参考第一个驱动程序文章

3.创建设备节点

  0)sudo -i #进入root用户

  1)cat /proc/devices | grep mychardev #查看主设备号

  2)mknod /dev/mychardev c 240 0 #通过设备号手动创建设备节点(后面会讲怎么通过在程序中自动创建设备节点)

  3)ls /dev/mychardev -l #查看创建的设备节点

  4)rm /dev/mychardev #手动删除设备节点(后面会讲怎么通过在程序中自动删除设备节点)

4.测试驱动程序

  1)shell程序调用驱动程序

  调用流程:用户命令→系统调用→内核处理→驱动执行

  echo "hello" > /dev/mychardev #将字符串"hello"写入驱动程序中的缓存区

  cat /dev/mychardev #读取驱动程序中缓存区数据并显示

  2)用户自定义程序调用驱动程序

  调用流程:用户程序→系统调用→内核处理→驱动执行

  3)这两种方式本质上都是通过系统调用接口去调用驱动程序的。

最新文章

随机文章