驱动开发硬核特训 · Day 16:字符设备驱动模型与实战注册流程


🎥 视频教程请关注 B 站:“嵌入式 Jerry”


一、为什么要学习字符设备驱动?

在 Linux 驱动开发中,字符设备(Character Device)驱动 是最基础也是最常见的一类驱动类型。很多设备(如 LED、按键、串口、传感器)都可以以字符设备方式呈现,并通过 read()write()ioctl() 与用户空间通信。

理解字符设备驱动,不仅是掌握 Linux 驱动开发的起点,也为日后深入 platform、总线设备驱动、misc、input 等模型打下坚实基础。


在这里插入图片描述

二、字符设备驱动的核心模型

字符设备驱动的注册模型由内核 include/linux/cdev.h 文件中的结构与接口决定:

2.1 struct cdev 是关键结构

struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    ...
};

它是内核维护的代表“一个字符设备”的核心结构体。

2.2 file_operations 是接口描述

这是用户空间调用 open/read/write/ioctl 等系统调用时,最终调用的驱动函数集。

static const struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .release = my_release,
};

2.3 主设备号 & 次设备号

  • 主设备号用于标识驱动程序
  • 次设备号用于区分同一类驱动下的不同设备实例

三、字符设备注册的五个步骤

学习字符设备注册的流程,可以总结为五步:

步骤 1:分配设备号

dev_t devno;
alloc_chrdev_region(&devno, 0, 1, "mychardev");
  • devno 是设备号(类型为 dev_t),用 MAJOR(devno)MINOR(devno) 分别提取主次设备号。
  • alloc_chrdev_region() 会自动分配主设备号。

步骤 2:初始化 cdev 结构

struct cdev my_cdev;
cdev_init(&my_cdev, &my_fops);
my_cdev.owner = THIS_MODULE;

步骤 3:添加 cdev 到系统中

cdev_add(&my_cdev, devno, 1);

这一步使得字符设备被内核正式管理。

步骤 4:创建设备节点

配合 udev 或手动创建设备节点:

mknod /dev/mydev c <主设备号> <次设备号>

更推荐在内核中通过 class_create + device_create 来自动创建节点。

步骤 5:卸载时释放

device_destroy(my_class, devno);
class_destroy(my_class);
cdev_del(&my_cdev);
unregister_chrdev_region(devno, 1);

四、完整实战代码:LED 字符设备驱动

我们用一个模拟的 LED 驱动为例,实现一个字符设备:

4.1 驱动核心代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>

#define DEV_NAME "myled"

static dev_t devno;
static struct cdev my_cdev;
static struct class *my_class;

static int led_value = 0;

static ssize_t led_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
    return copy_to_user(buf, &led_value, sizeof(led_value)) ? -EFAULT : sizeof(led_value);
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
    return copy_from_user(&led_value, buf, sizeof(led_value)) ? -EFAULT : sizeof(led_value);
}

static int led_open(struct inode *inode, struct file *file)
{
    pr_info("LED device opened\n");
    return 0;
}

static int led_release(struct inode *inode, struct file *file)
{
    pr_info("LED device closed\n");
    return 0;
}

static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .read = led_read,
    .write = led_write,
    .open = led_open,
    .release = led_release,
};

static int __init led_init(void)
{
    alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
    cdev_init(&my_cdev, &led_fops);
    cdev_add(&my_cdev, devno, 1);

    my_class = class_create(THIS_MODULE, "myled_class");
    device_create(my_class, NULL, devno, NULL, DEV_NAME);

    pr_info("LED driver loaded: major=%d\n", MAJOR(devno));
    return 0;
}

static void __exit led_exit(void)
{
    device_destroy(my_class, devno);
    class_destroy(my_class);
    cdev_del(&my_cdev);
    unregister_chrdev_region(devno, 1);

    pr_info("LED driver unloaded\n");
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jerry");
MODULE_DESCRIPTION("Simple LED Char Device Driver");

五、测试方法与验证

编译并加载

make
insmod myled.ko

查看设备节点

ls /dev/myled

读写测试

echo 1 > /dev/myled
cat /dev/myled

六、文件系统中的呈现:/proc 与 /sys

字符设备驱动在加载后,会自动出现在以下路径中:

路径含义
/proc/devices显示当前注册的主设备号及驱动名
/sys/class/myled_class/myled/设备节点属性
/dev/myled用户空间访问入口

七、理论总结:字符设备模型的特点

  • 与平台设备无绑定关系,适合用于最简设备建模。
  • 所有用户空间 I/O 都通过 file_operations 映射函数实现。
  • 可作为更复杂模型(如 misc、input、platform)的基础。

八、常见问题答疑

Q1:字符设备必须创建设备节点吗?

A:必须。否则无法从用户空间访问。可使用 mknoddevice_create 自动生成。

Q2:file_operations 中哪些函数必须实现?

A:至少实现 openreleasereadwrite。也可以只实现 ioctl,视业务而定。

Q3:主设备号可以固定吗?

A:可以使用 register_chrdev() 指定固定主设备号,但建议使用动态分配。


九、结语与展望

字符设备模型虽是最基础的驱动类型,但它揭示了驱动与用户空间之间的交互机制,是理解 Linux 驱动架构的入口。后续我们将继续向上探索:

  • misc 模型
  • platform 总线匹配机制
  • device tree 配合 platform 驱动

敬请期待《驱动开发硬核特训 · Day 17》!


📺 视频教程请关注 B 站:“嵌入式 Jerry”

如有问题,欢迎评论区留言交流,也欢迎点赞收藏,让我们一起成为 Linux 驱动高手!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值