1. Linux驱动开发核心结构体速查手册
作为一名在Linux驱动开发领域摸爬滚打多年的工程师,我深知内核API的复杂性。每次开发新驱动时,总要反复查阅各种结构体和函数的用法。今天我就把自己多年积累的高频结构体/宏/函数速查表分享给大家,这可能是你见过最实用的Linux驱动开发备忘录。
2. VFS基础对象解析
2.1 struct file:打开实例对象
struct file是驱动开发中最常打交道的对象之一,它代表每次open()系统调用创建的打开实例。理解它的生命周期对驱动稳定性至关重要。
关键细节:
- 创建时机:成功open()时由内核创建
- 销毁时机:所有引用关闭(close()+引用计数归零)
- 典型用法:
c复制static int my_open(struct inode *inode, struct file *filp)
{
struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev);
filp->private_data = dev;
// ...
}
重要成员解析:
f_op:必须初始化为你的file_operations表private_data:驱动私有数据的黄金位置f_flags:需要特别检查O_NONBLOCK标志f_owner:实现异步通知(SIGIO)的关键
2.2 struct inode:文件元数据
inode承载着文件的元信息,对字符设备驱动来说,最重要的是它能提供设备号和关联的cdev指针。
实用技巧:
c复制static int my_open(struct inode *inode, struct file *filp)
{
// 从inode获取设备号的两种方式
dev_t devno = inode->i_rdev;
dev_t devno = imajor(inode), iminor(inode);
// 通过i_cdev找到设备对象
struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev);
// ...
}
2.3 file_operations:驱动操作集
这是驱动与VFS的契约,定义了所有可能的操作接口。现代内核推荐只实现必要的回调。
典型实现模板:
c复制static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
.unlocked_ioctl = my_ioctl,
.poll = my_poll,
.llseek = no_llseek, // 不支持seek
};
注意事项:
- 必须设置.owner防止模块被意外卸载
- 不支持的操作用NULL填充(如不实现mmap)
- unlocked_ioctl已取代旧的ioctl方法
3. 字符设备注册全流程
3.1 设备号分配与管理
设备号是字符设备的身份证,管理好它是驱动稳定的第一步。
推荐做法:
c复制#define MY_DEVICE_NAME "mydev"
#define MY_MAX_DEVS 4
static dev_t my_devno;
static struct class *my_class;
static int __init my_init(void)
{
// 动态申请设备号(推荐)
int err = alloc_chrdev_region(&my_devno, 0, MY_MAX_DEVS, MY_DEVICE_NAME);
if (err < 0) {
pr_err("Failed to allocate char device region\n");
return err;
}
// 创建设备类
my_class = class_create(THIS_MODULE, MY_DEVICE_NAME);
if (IS_ERR(my_class)) {
unregister_chrdev_region(my_devno, MY_MAX_DEVS);
return PTR_ERR(my_class);
}
// ...
}
3.2 cdev初始化与注册
cdev是连接设备号和file_operations的桥梁。
完整示例:
c复制struct my_dev {
struct cdev cdev;
// 其他设备特定数据
};
static int my_setup_cdev(struct my_dev *dev, int index)
{
dev_t devno = MKDEV(MAJOR(my_devno), MINOR(my_devno) + index);
cdev_init(&dev->cdev, &my_fops);
dev->cdev.owner = THIS_MODULE;
int err = cdev_add(&dev->cdev, devno, 1);
if (err) {
pr_err("Error %d adding cdev\n", err);
return err;
}
// 自动创建设备节点
device_create(my_class, NULL, devno, NULL, "mydev%d", index);
return 0;
}
3.3 miscdevice简化方案
对于单一设备的简单驱动,miscdevice可以大幅简化流程:
c复制static struct miscdevice my_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mydev",
.fops = &my_fops,
};
static int __init my_init(void)
{
return misc_register(&my_miscdev);
}
优势:
- 自动处理设备号分配
- 自动创建/dev节点
- 无需手动管理cdev和class
4. 驱动上下文管理技巧
4.1 container_of的妙用
这个宏是Linux内核中的"黑魔法",可以从结构体成员反推外层结构。
典型场景:
c复制struct my_dev {
struct cdev cdev;
struct mutex lock;
// ...
};
static int my_open(struct inode *inode, struct file *filp)
{
struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev);
filp->private_data = dev;
// ...
}
4.2 引用计数管理
对于设备对象的生命周期管理,内核提供了两种主要方案:
- refcount_t(更安全):
c复制struct my_dev {
refcount_t refcnt;
// ...
};
// 初始化
refcount_set(&dev->refcnt, 1);
// 增加引用
refcount_inc(&dev->refcnt);
// 减少引用
if (refcount_dec_and_test(&dev->refcnt)) {
kfree(dev);
}
- kref(更传统):
c复制struct my_dev {
struct kref kref;
// ...
};
void my_dev_release(struct kref *kref)
{
struct my_dev *dev = container_of(kref, struct my_dev, kref);
kfree(dev);
}
// 使用示例
kref_put(&dev->kref, my_dev_release);
5. 用户空间交互关键技术
5.1 安全数据拷贝
内核与用户空间的数据交换必须使用专用API:
c复制// 从用户空间拷贝数据
if (copy_from_user(kernel_buf, user_buf, count)) {
return -EFAULT;
}
// 向用户空间拷贝数据
if (copy_to_user(user_buf, kernel_buf, count)) {
return -EFAULT;
}
// 单个值操作
int val;
if (get_user(val, (int __user *)arg))
return -EFAULT;
5.2 高级用户空间辅助函数
内核提供了一些便捷函数简化开发:
c复制// 复制用户空间字符串
char *kernel_str = strndup_user(user_str, MAX_LEN);
if (IS_ERR(kernel_str))
return PTR_ERR(kernel_str);
// 复制用户空间内存块
void *kernel_buf = memdup_user(user_buf, size);
if (IS_ERR(kernel_buf))
return PTR_ERR(kernel_buf);
6. 并发控制机制详解
6.1 互斥锁(mutex)最佳实践
mutex是驱动开发中最常用的锁机制:
c复制struct my_dev {
struct mutex lock;
// ...
};
static int my_open(struct inode *inode, struct file *filp)
{
struct my_dev *dev = filp->private_data;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
// 临界区代码
mutex_unlock(&dev->lock);
return 0;
}
注意事项:
- 优先使用mutex_lock_interruptible(),允许信号中断
- 临界区代码要尽量简短
- 避免嵌套加锁
6.2 自旋锁(spinlock)使用场景
自旋锁适用于不可睡眠的上下文(如中断处理):
c复制struct my_dev {
spinlock_t lock;
// ...
};
irqreturn_t my_interrupt(int irq, void *dev_id)
{
struct my_dev *dev = dev_id;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
// 中断上下文临界区
spin_unlock_irqrestore(&dev->lock, flags);
return IRQ_HANDLED;
}
关键点:
- 中断上下文必须使用spin_lock_irqsave()变体
- 临界区必须非常短(通常只是设置标志或调度工作队列)
7. 阻塞I/O实现模式
7.1 等待队列基础实现
实现阻塞读的典型模式:
c复制struct my_dev {
wait_queue_head_t readq;
bool data_ready;
// ...
};
static ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
struct my_dev *dev = filp->private_data;
if (wait_event_interruptible(dev->readq, dev->data_ready))
return -ERESTARTSYS;
// 数据已就绪,进行拷贝
// ...
dev->data_ready = false;
return copied;
}
// 数据到达时(可能在中断上下文)
static void data_arrived(struct my_dev *dev)
{
dev->data_ready = true;
wake_up_interruptible(&dev->readq);
}
7.2 poll/select支持
实现.poll回调支持多路复用:
c复制static __poll_t my_poll(struct file *filp, poll_table *wait)
{
struct my_dev *dev = filp->private_data;
__poll_t mask = 0;
poll_wait(filp, &dev->readq, wait);
if (dev->data_ready)
mask |= POLLIN | POLLRDNORM;
return mask;
}
8. 高级特性实现技巧
8.1 异步通知(fasync)实现
支持SIGIO信号通知:
c复制struct my_dev {
struct fasync_struct *async_queue;
// ...
};
static int my_fasync(int fd, struct file *filp, int on)
{
struct my_dev *dev = filp->private_data;
return fasync_helper(fd, filp, on, &dev->async_queue);
}
// 数据到达时发送信号
static void signal_data_ready(struct my_dev *dev)
{
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
8.2 mmap内存映射
实现设备内存映射到用户空间:
c复制static int my_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct my_dev *dev = filp->private_data;
// 检查请求的映射范围是否合法
if (vma->vm_end - vma->vm_start > dev->buf_size)
return -EINVAL;
// 设置页保护标志
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
// 映射物理连续内存
return remap_pfn_range(vma, vma->vm_start,
virt_to_phys(dev->buf) >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
}
9. 中断处理最佳实践
9.1 中断注册与处理
标准中断处理流程:
c复制static irqreturn_t my_interrupt(int irq, void *dev_id)
{
struct my_dev *dev = dev_id;
// 读取中断状态
u32 status = readl(dev->regs + STATUS_REG);
if (!(status & INT_MASK))
return IRQ_NONE; // 不是我们的中断
// 清除中断
writel(status & INT_MASK, dev->regs + STATUS_REG);
// 处理中断
// ...
return IRQ_HANDLED;
}
static int my_probe(struct platform_device *pdev)
{
// ...
int irq = platform_get_irq(pdev, 0);
int err = request_irq(irq, my_interrupt, IRQF_SHARED,
dev_name(&pdev->dev), dev);
if (err) {
dev_err(&pdev->dev, "Cannot request IRQ\n");
return err;
}
// ...
}
9.2 工作队列使用
将耗时任务推迟到进程上下文:
c复制struct my_work {
struct work_struct work;
struct my_dev *dev;
// 其他工作数据
};
static void my_work_handler(struct work_struct *work)
{
struct my_work *my_work = container_of(work, struct my_work, work);
struct my_dev *dev = my_work->dev;
// 处理耗时任务
// ...
kfree(my_work);
}
static irqreturn_t my_interrupt(int irq, void *dev_id)
{
struct my_dev *dev = dev_id;
struct my_work *work;
work = kmalloc(sizeof(*work), GFP_ATOMIC);
if (!work)
return IRQ_HANDLED;
INIT_WORK(&work->work, my_work_handler);
work->dev = dev;
schedule_work(&work->work);
return IRQ_HANDLED;
}
10. 设备树与平台驱动
10.1 平台驱动框架
现代Linux驱动推荐使用平台设备框架:
c复制static const struct of_device_id my_of_match[] = {
{ .compatible = "vendor,my-device" },
{ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
static struct platform_driver my_driver = {
.driver = {
.name = "my-device",
.of_match_table = my_of_match,
},
.probe = my_probe,
.remove = my_remove,
};
module_platform_driver(my_driver);
10.2 设备树资源获取
从设备树获取资源的标准方法:
c复制static int my_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *regs;
// 获取内存区域
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENXIO;
// 映射寄存器
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
// 获取中断
int irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
// 获取设备树属性
u32 param;
if (of_property_read_u32(dev->of_node, "my-param", ¶m))
param = DEFAULT_VALUE;
// ...
}
11. 调试与测试技巧
11.1 sysfs接口创建
通过sysfs暴露调试信息:
c复制static ssize_t debug_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct my_dev *my_dev = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", my_dev->debug_value);
}
static ssize_t debug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct my_dev *my_dev = dev_get_drvdata(dev);
int err = kstrtoint(buf, 0, &my_dev->debug_value);
if (err)
return err;
return count;
}
static DEVICE_ATTR_RW(debug);
static int my_probe(struct platform_device *pdev)
{
// ...
int err = device_create_file(&pdev->dev, &dev_attr_debug);
if (err)
return err;
// ...
}
11.2 debugfs使用
创建调试专用文件节点:
c复制static struct dentry *debug_dir;
static int debugfs_show(struct seq_file *m, void *v)
{
struct my_dev *dev = m->private;
seq_printf(m, "Status: %08x\n", dev->status);
seq_printf(m, "Buffer size: %zu\n", dev->buf_size);
// ...
return 0;
}
static int debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, debugfs_show, inode->i_private);
}
static const struct file_operations debugfs_fops = {
.open = debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int my_probe(struct platform_device *pdev)
{
// ...
debug_dir = debugfs_create_dir("mydev", NULL);
debugfs_create_file("status", 0400, debug_dir, dev, &debugfs_fops);
// ...
}
12. 驱动开发实用模板
12.1 独占打开实现
防止设备被多次打开:
c复制static int my_open(struct inode *inode, struct file *filp)
{
struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev);
if (test_and_set_bit(0, &dev->open_flag))
return -EBUSY;
filp->private_data = dev;
return 0;
}
static int my_release(struct inode *inode, struct file *filp)
{
struct my_dev *dev = filp->private_data;
clear_bit(0, &dev->open_flag);
return 0;
}
12.2 环形缓冲区实现
使用kfifo实现生产者-消费者模型:
c复制struct my_dev {
struct kfifo fifo;
wait_queue_head_t readq;
struct mutex lock;
// ...
};
static int my_init(struct my_dev *dev)
{
int err = kfifo_alloc(&dev->fifo, BUFFER_SIZE, GFP_KERNEL);
if (err)
return err;
init_waitqueue_head(&dev->readq);
mutex_init(&dev->lock);
return 0;
}
static ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
struct my_dev *dev = filp->private_data;
unsigned int copied;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
if (kfifo_is_empty(&dev->fifo)) {
mutex_unlock(&dev->lock);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(dev->readq, !kfifo_is_empty(&dev->fifo)))
return -ERESTARTSYS;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
}
if (kfifo_to_user(&dev->fifo, buf, count, &copied))
copied = -EFAULT;
mutex_unlock(&dev->lock);
return copied;
}
static ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
struct my_dev *dev = filp->private_data;
unsigned int copied;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
if (kfifo_from_user(&dev->fifo, buf, count, &copied))
copied = -EFAULT;
mutex_unlock(&dev->lock);
if (copied > 0)
wake_up_interruptible(&dev->readq);
return copied;
}
13. 错误处理与资源管理
13.1 devm资源管理
使用设备管理资源简化错误处理:
c复制static int my_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct my_dev *my_dev;
int err;
my_dev = devm_kzalloc(dev, sizeof(*my_dev), GFP_KERNEL);
if (!my_dev)
return -ENOMEM;
my_dev->regs = devm_ioremap_resource(dev, platform_get_resource(pdev, IORESOURCE_MEM, 0));
if (IS_ERR(my_dev->regs))
return PTR_ERR(my_dev->regs);
my_dev->irq = platform_get_irq(pdev, 0);
if (my_dev->irq < 0)
return my_dev->irq;
err = devm_request_irq(dev, my_dev->irq, my_interrupt, 0, dev_name(dev), my_dev);
if (err)
return err;
// 其他初始化...
platform_set_drvdata(pdev, my_dev);
return 0;
}
static int my_remove(struct platform_device *pdev)
{
// 所有资源都会自动释放
return 0;
}
13.2 错误码处理
Linux内核错误处理规范:
c复制static int my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct my_dev *dev = filp->private_data;
switch (cmd) {
case MY_CMD_1:
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
// ...
mutex_unlock(&dev->lock);
return 0;
case MY_CMD_2:
if (!access_ok((void __user *)arg, sizeof(struct my_data)))
return -EFAULT;
// ...
return 0;
default:
return -ENOTTY; // 未知命令
}
}
14. 性能优化技巧
14.1 高效内存分配
根据场景选择合适的内存分配方式:
c复制// 小对象频繁分配(不可睡眠上下文)
void *p = kmalloc(size, GFP_ATOMIC);
// 小对象频繁分配(可睡眠上下文)
void *p = kmalloc(size, GFP_KERNEL);
// 大内存分配(物理连续)
void *p = kmalloc(size, GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
// 非常大的内存分配(可能失败)
void *p = vmalloc(size);
// DMA内存分配
void *p = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
14.2 延迟执行策略
根据延迟需求选择合适的机制:
c复制// 毫秒级延迟(进程上下文)
msleep(20); // 不可中断
msleep_interruptible(20); // 可被信号中断
// 精确微秒级延迟
udelay(50); // 忙等待(短延迟)
usleep_range(50, 100); // 非忙等待(推荐)
// 内核定时器(异步延迟执行)
struct timer_list my_timer;
static void my_timer_callback(struct timer_list *t)
{
// 定时器处理
}
// 初始化
timer_setup(&my_timer, my_timer_callback, 0);
mod_timer(&my_timer, jiffies + msecs_to_jiffies(100));
// 高精度定时器
struct hrtimer my_hrtimer;
static enum hrtimer_restart my_hrtimer_callback(struct hrtimer *timer)
{
// 定时器处理
return HRTIMER_NORESTART;
}
// 初始化
hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
my_hrtimer.function = &my_hrtimer_callback;
hrtimer_start(&my_hrtimer, ms_to_ktime(10), HRTIMER_MODE_REL);
15. 跨版本兼容性处理
15.1 内核API变化适配
处理不同内核版本间的API差异:
c复制// 检测内核版本
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
// 旧内核兼容代码
#else
// 新内核代码
#endif
// 处理file_operations变化
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
.proc_read = my_read,
#else
.read = my_read,
#endif
// 处理中断处理函数原型变化
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
static irqreturn_t my_interrupt(int irq, void *dev_id)
#else
static irqreturn_t my_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
// ...
}
15.2 模块签名与安全
现代内核的安全要求:
c复制// 模块元信息
MODULE_AUTHOR("Your Name <your.email@example.com>");
MODULE_DESCRIPTION("Description of your driver");
MODULE_LICENSE("GPL v2"); // 必须使用GPL兼容许可证
// 导出符号(谨慎使用)
EXPORT_SYMBOL(my_exported_function);
// 模块参数
static int debug_level = 1;
module_param(debug_level, int, 0644);
MODULE_PARM_DESC(debug_level, "Debug message level (0-3)");
// 设备树匹配表
static const struct of_device_id my_of_match[] = {
{ .compatible = "vendor,my-device" },
{ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
16. 驱动测试与验证
16.1 单元测试框架
使用内核自带的测试框架:
c复制#include <linux/kunit/test.h>
static void my_test_case(struct kunit *test)
{
struct my_dev *dev = test->priv;
int result;
// 测试用例1
result = my_function(dev, TEST_VALUE);
KUNIT_EXPECT_EQ(test, result, EXPECTED_RESULT);
// 测试用例2
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev->buffer);
}
static struct kunit_case my_test_cases[] = {
KUNIT_CASE(my_test_case),
{}
};
static struct kunit_suite my_test_suite = {
.name = "my_driver_test",
.init = my_test_init,
.exit = my_test_exit,
.test_cases = my_test_cases,
};
kunit_test_suite(my_test_suite);
16.2 用户空间测试工具
编写配套测试程序:
c复制// test_mydev.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("/dev/mydev", O_RDWR);
if (fd < 0) {
perror("open failed");
return 1;
}
// 测试写入
char buf[128] = "test data";
if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
perror("write failed");
close(fd);
return 1;
}
// 测试读取
if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
perror("read failed");
close(fd);
return 1;
}
close(fd);
return 0;
}
17. 持续维护与升级
17.1 变更日志维护
良好的变更记录习惯:
c复制/*
* ChangeLog:
* v1.0.0 - 2023-01-01
* - Initial version
* v1.1.0 - 2023-02-15
* - Added support for new hardware revision
* - Fixed race condition in interrupt handler
* v1.2.0 - 2023-03-20
* - Implemented power management callbacks
* - Improved error handling
*/
17.2 提交补丁流程
参与上游内核开发的建议:
- 使用git管理代码
- 遵循内核编码风格(checkpatch.pl)
- 编写有意义的提交信息
- 通过邮件列表提交补丁
示例提交信息:
code复制Subject: [PATCH] drivers/misc: Add support for new features in mydev
This patch adds the following improvements to the mydev driver:
1. Support for hardware v2.0 features
2. Better power management
3. Fix for a race condition in the IRQ handler
The changes have been tested on x86 and ARM platforms.
Signed-off-by: Your Name <your.email@example.com>
18. 实用资源推荐
18.1 参考书籍
- 《Linux Device Drivers, 3rd Edition》
- 《Essential Linux Device Drivers》
- 《Linux Kernel Development》
18.2 在线资源
- Kernel documentation (Documentation/driver-api/)
- LWN.net (https://lwn.net/)
- Elixir Bootlin (https://elixir.bootlin.com/)