Linux 驱动开发流程(带最小可运行代码 + 通俗类比)

Linux 驱动开发流程(带最小可运行代码 + 通俗类比)

很多人学 Linux 驱动都会卡在这里:

API 都看过,但完全不知道它们是怎么串起来工作的

这篇文章目标很明确:

✅ 用一条主线讲清流程
✅ 用类比帮你记住
✅ 给你一个最小可运行驱动模板


一、先记住一条主线(最重要)

用户操作 → 内核 → 设备号 → cdev → file_operations → 你的函数

二、一个类比彻底记住

🎯 类比:打电话

现实Linux
电话号码设备号
通讯录cdev
file_operations
打电话open/read/write

📞 过程:

open("/dev/mydev")
   ↓
设备号(号码)
   ↓
cdev(通讯录)
   ↓
file_operations(人)
   ↓
调用 read/write

三、驱动开发完整流程(工程版)

① 枚举设备(DTS / 总线)
② 注册驱动
③ 匹配(compatible)
④ probe() 执行
⑤ 注册字符设备(cdev)
⑥ 创建 /dev 节点
⑦ 用户操作 → 调用驱动

四、最小字符设备驱动(核心)

下面这个代码你可以直接用来练手👇


🔧 1. 驱动代码(mydev.c)

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

#define DEVICE_NAME "mydev"

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

/* ---------------- 核心操作函数 ---------------- */

static int my_open(struct inode *inode, struct file *file)
{
    printk("my_open called\n");
    return 0;
}

static ssize_t my_read(struct file *file, char __user *buf, size_t size, loff_t *off)
{
    printk("my_read called\n");
    return 0;
}

static ssize_t my_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{
    printk("my_write called, size=%ld\n", size);
    return size;
}

/* file_operations */
static struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .read = my_read,
    .write = my_write,
};

/* ---------------- 初始化 ---------------- */

static int __init my_init(void)
{
    printk("mydev init\n");

    /* 1️⃣ 分配设备号 */
    alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);

    /* 2️⃣ 初始化 cdev */
    cdev_init(&my_cdev, &my_fops);

    /* 3️⃣ 添加 cdev */
    cdev_add(&my_cdev, dev_num, 1);

    /* 4️⃣ 创建设备节点 */
    my_class = class_create(THIS_MODULE, "my_class");
    device_create(my_class, NULL, dev_num, NULL, DEVICE_NAME);

    return 0;
}

/* ---------------- 退出 ---------------- */

static void __exit my_exit(void)
{
    device_destroy(my_class, dev_num);
    class_destroy(my_class);

    cdev_del(&my_cdev);
    unregister_chrdev_region(dev_num, 1);

    printk("mydev exit\n");
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");

五、这个代码到底在干嘛?(一行一行串起来)


1️⃣ 分配设备号

alloc_chrdev_region(&dev_num, 0, 1, "mydev");

👉 作用:

给设备分配一个“身份证”(major + minor)

2️⃣ 定义行为(file_operations)

static struct file_operations my_fops

👉 定义:

这个设备能干什么(open/read/write)

3️⃣ cdev_init(核心)

cdev_init(&my_cdev, &my_fops);

👉 本质:

把“设备号”和“操作函数”绑定

4️⃣ cdev_add(关键)

cdev_add(&my_cdev, dev_num, 1);

👉 本质:

告诉内核:这个设备可以用了

5️⃣ 创建设备节点

device_create(...)

👉 最终生成:

/dev/mydev

六、真正的数据流(必须理解)

当你执行:

echo 123 > /dev/mydev

内核发生:

write("/dev/mydev")
   ↓
找到设备号
   ↓
找到 cdev
   ↓
找到 file_operations
   ↓
调用 my_write()

七、cdev 的本质(终极总结)

cdev = 内核里的“设备通讯录”

它做了一件事:

设备号 → file_operations

八、你必须能复述的三句话


✅ 1️⃣

file_operations = 定义设备“能干什么”


✅ 2️⃣

设备号 = 标识“是谁”


✅ 3️⃣(最重要)

cdev = 把“是谁”和“能干什么”绑定起来


九、扩展:和平台驱动的关系

字符设备只是:

“用户接口层”

完整驱动其实是:

DTS → platform_driver → probe → 注册 cdev → /dev

十、为什么你之前学不会?

因为你之前是这样记的:

背 API:cdev_add、cdev_init

但正确理解应该是:

用户调用 → 设备号 → cdev → file_operations → 函数

十一、建议练习(强烈推荐)

insmod mydev.ko
echo hello > /dev/mydev
dmesg

你会看到:

my_write called

十二、一句话总结(面试可用)

Linux 字符设备驱动的核心是通过 cdev,将设备号映射到 file_operations,从而让用户空间调用最终执行驱动代码。


结尾

如果你能讲清这三件事,你已经超过 70% 初级驱动工程师:

① 设备怎么来的(枚举)
② probe 为什么执行(匹配)
③ read/write 为什么能调用(cdev)

这才是驱动开发的“主线思维”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值