1.私有数据

在Linux驱动开发中,私有数据(private data) 是一个重要概念,它允许驱动程序为每个设备实例存储特定的信息。以下是详细说明:

1. 私有数据的概念

私有数据是一个指向任意数据结构的指针,通常用于:

  • 存储设备特定的信息
  • 在文件操作函数之间传递数据
  • 管理设备的硬件资源
  • 保持设备状态信息

2. 设置和获取私有数据

2.1 在设备结构体中设置私有数据

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

struct my_device_data {
    struct cdev cdev;
    int device_id;
    void __iomem *reg_base;
    spinlock_t lock;
    // 其他设备特定数据
};

static int my_open(struct inode *inode, struct file *filp)
{
    struct my_device_data *dev_data;
    
    // 从inode获取cdev,然后获取包含cdev的设备结构体
    dev_data = container_of(inode->i_cdev, struct my_device_data, cdev);
    
    // 将设备数据存储为文件的私有数据
    filp->private_data = dev_data;
    
    return 0;
}

2.2 在probe函数中设置

static int my_probe(struct platform_device *pdev)
{
    struct my_device_data *dev_data;
    int ret;
    
    // 分配设备数据结构
    dev_data = devm_kzalloc(&pdev->dev, sizeof(*dev_data), GFP_KERNEL);
    if (!dev_data)
        return -ENOMEM;
    
    // 初始化设备数据
    dev_data->device_id = 0;
    spin_lock_init(&dev_data->lock);
    
    // 将私有数据存储到平台设备中
    platform_set_drvdata(pdev, dev_data);
    
    // 硬件初始化
    dev_data->reg_base = devm_platform_ioremap_resource(pdev, 0);
    if (IS_ERR(dev_data->reg_base))
        return PTR_ERR(dev_data->reg_base);
    
    return 0;
}

3. 在文件操作中使用私有数据

static ssize_t my_read(struct file *filp, char __user *buf, 
                      size_t count, loff_t *f_pos)
{
    struct my_device_data *dev_data = filp->private_data;
    ssize_t ret;
    
    if (!dev_data)
        return -ENODEV;
    
    spin_lock(&dev_data->lock);
    // 使用设备数据进行读取操作
    // ...
    spin_unlock(&dev_data->lock);
    
    return ret;
}

static ssize_t my_write(struct file *filp, const char __user *buf,
                       size_t count, loff_t *f_pos)
{
    struct my_device_data *dev_data = filp->private_data;
    
    if (!dev_data)
        return -ENODEV;
    
    // 使用设备数据进行写入操作
    // ...
    
    return count;
}

static int my_release(struct inode *inode, struct file *filp)
{
    // 清除私有数据引用
    filp->private_data = NULL;
    return 0;
}

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

4. 完整的驱动示例

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

#define DEVICE_NAME "mydevice"
#define MAX_DEVICES 2

struct my_device_data {
    struct cdev cdev;
    struct device *device;
    int device_id;
    unsigned long usage_count;
    spinlock_t lock;
    char buffer[1024];
};

static int major;
static struct class *my_class;
static struct my_device_data devices[MAX_DEVICES];

static int my_open(struct inode *inode, struct file *filp)
{
    struct my_device_data *dev_data;
    
    dev_data = container_of(inode->i_cdev, struct my_device_data, cdev);
    filp->private_data = dev_data;
    
    spin_lock(&dev_data->lock);
    dev_data->usage_count++;
    spin_unlock(&dev_data->lock);
    
    printk(KERN_INFO "Device %d opened, usage count: %lu\n",
           dev_data->device_id, dev_data->usage_count);
    
    return 0;
}

static int my_release(struct inode *inode, struct file *filp)
{
    struct my_device_data *dev_data = filp->private_data;
    
    spin_lock(&dev_data->lock);
    dev_data->usage_count--;
    spin_unlock(&dev_data->lock);
    
    printk(KERN_INFO "Device %d closed, usage count: %lu\n",
           dev_data->device_id, dev_data->usage_count);
    
    return 0;
}

static ssize_t my_read(struct file *filp, char __user *buf,
                      size_t count, loff_t *f_pos)
{
    struct my_device_data *dev_data = filp->private_data;
    ssize_t ret;
    
    if (*f_pos >= sizeof(dev_data->buffer))
        return 0;
    
    if (*f_pos + count > sizeof(dev_data->buffer))
        count = sizeof(dev_data->buffer) - *f_pos;
    
    if (copy_to_user(buf, dev_data->buffer + *f_pos, count))
        return -EFAULT;
    
    *f_pos += count;
    return count;
}

static ssize_t my_write(struct file *filp, const char __user *buf,
                       size_t count, loff_t *f_pos)
{
    struct my_device_data *dev_data = filp->private_data;
    
    if (*f_pos >= sizeof(dev_data->buffer))
        return -ENOSPC;
    
    if (*f_pos + count > sizeof(dev_data->buffer))
        count = sizeof(dev_data->buffer) - *f_pos;
    
    if (copy_from_user(dev_data->buffer + *f_pos, buf, count))
        return -EFAULT;
    
    *f_pos += count;
    return count;
}

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

static int __init my_init(void)
{
    int i, ret;
    dev_t dev;
    
    // 分配设备号
    ret = alloc_chrdev_region(&dev, 0, MAX_DEVICES, DEVICE_NAME);
    if (ret < 0)
        return ret;
    
    major = MAJOR(dev);
    
    // 创建设备类
    my_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(my_class)) {
        unregister_chrdev_region(dev, MAX_DEVICES);
        return PTR_ERR(my_class);
    }
    
    // 初始化每个设备
    for (i = 0; i < MAX_DEVICES; i++) {
        devices[i].device_id = i;
        spin_lock_init(&devices[i].lock);
        devices[i].usage_count = 0;
        memset(devices[i].buffer, 0, sizeof(devices[i].buffer));
        
        // 初始化cdev
        cdev_init(&devices[i].cdev, &my_fops);
        devices[i].cdev.owner = THIS_MODULE;
        
        // 添加cdev到系统
        ret = cdev_add(&devices[i].cdev, MKDEV(major, i), 1);
        if (ret) {
            printk(KERN_ERR "Failed to add cdev for device %d\n", i);
            continue;
        }
        
        // 创建设备节点
        devices[i].device = device_create(my_class, NULL, 
                                        MKDEV(major, i), NULL,
                                        "mydevice%d", i);
        if (IS_ERR(devices[i].device)) {
            cdev_del(&devices[i].cdev);
            printk(KERN_ERR "Failed to create device for device %d\n", i);
        }
    }
    
    printk(KERN_INFO "My device driver loaded\n");
    return 0;
}

static void __exit my_exit(void)
{
    int i;
    dev_t dev = MKDEV(major, 0);
    
    for (i = 0; i < MAX_DEVICES; i++) {
        if (devices[i].device)
            device_destroy(my_class, MKDEV(major, i));
        cdev_del(&devices[i].cdev);
    }
    
    class_destroy(my_class);
    unregister_chrdev_region(dev, MAX_DEVICES);
    
    printk(KERN_INFO "My device driver unloaded\n");
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Example driver using private data");

5. 关键要点

  1. 线程安全:在多线程环境中访问私有数据时,需要使用适当的锁机制
  2. 内存管理:确保在设备生命周期内私有数据保持有效
  3. 错误处理:始终检查私有数据指针是否为NULL
  4. 资源清理:在模块卸载时正确释放所有资源

私有数据是Linux驱动开发中的核心概念,正确使用它可以创建更加模块化和可维护的驱动程序。


2.Linux并发与竞争

1. 并发与竞争概述

1.1 基本概念

  • 并发:多个执行单元同时或并行执行
  • 竞争:多个执行单元对共享资源的同时访问导致的不确定性
  • 临界区:访问共享资源的代码段
  • 竞态条件:执行结果依赖于并发执行顺序的情况

1.2 并发来源

/* 并发的主要来源 */
1. 多核CPU真正并行执行
2. 单核CPU上的任务抢占
3. 中断处理程序
4. 底半部机制(软中断、tasklet、工作队列)
5. 内核抢占
6. 睡眠与唤醒

2. 内核同步机制

2.1 原子操作

2.1.1 整型原子操作

#include <linux/atomic.h>

atomic_t counter = ATOMIC_INIT(0);

// 基本操作
void atomic_set(atomic_t *v, int i);
int atomic_read(atomic_t *v);

// 算术操作
void atomic_add(int i, atomic_t *v);
void atomic_sub(int i, atomic_t *v);
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);

// 返回操作结果
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);

// 测试操作
int atomic_inc_and_test(atomic_t *v);  // 结果为0返回1
int atomic_dec_and_test(atomic_t *v);  // 结果为0返回1

2.1.2 位原子操作

#include <linux/bitops.h>

unsigned long word = 0;

void set_bit(int nr, volatile unsigned long *addr);
void clear_bit(int nr, volatile unsigned long *addr);
void change_bit(int nr, volatile unsigned long *addr);

int test_bit(int nr, volatile unsigned long *addr);
int test_and_set_bit(int nr, volatile unsigned long *addr);
int test_and_clear_bit(int nr, volatile unsigned long *addr);

2.2 自旋锁

2.2.1 基本自旋锁

#include <linux/spinlock.h>

DEFINE_SPINLOCK(my_lock);
unsigned long flags;

// 获取锁
spin_lock(&my_lock);
// 临界区
spin_unlock(&my_lock);

// 中断上下文安全版本
spin_lock_irqsave(&my_lock, flags);    // 保存中断状态并禁用中断
// 临界区
spin_unlock_irqrestore(&my_lock, flags); // 恢复中断状态

// 简单版本(已知中断未使能)
spin_lock_irq(&my_lock);
spin_unlock_irq(&my_lock);

2.2.2 读写自旋锁

#include <linux/rwlock.h>

DEFINE_RWLOCK(my_rwlock);

// 读者
read_lock(&my_rwlock);
// 读取临界区
read_unlock(&my_rwlock);

// 写者
write_lock(&my_rwlock);
// 写入临界区
write_unlock(&my_rwlock);

2.3 信号量

2.3.1 计数信号量

#include <linux/semaphore.h>

struct semaphore my_sem;

// 初始化
sema_init(&my_sem, 1);  // 二进制信号量
sema_init(&my_sem, 5);  // 计数信号量

// 获取信号量
void down(struct semaphore *sem);           // 不可中断
int down_interruptible(struct semaphore *sem); // 可被信号中断
int down_trylock(struct semaphore *sem);    // 非阻塞

// 释放信号量
void up(struct semaphore *sem);

2.3.2 互斥信号量

#include <linux/mutex.h>

struct mutex my_mutex;

// 初始化
mutex_init(&my_mutex);

// 使用
mutex_lock(&my_mutex);
// 临界区
mutex_unlock(&my_mutex);

// 非阻塞版本
if (mutex_trylock(&my_mutex)) {
    // 临界区
    mutex_unlock(&my_mutex);
} else {
    // 处理无法获取锁的情况
}

2.4 完成量

#include <linux/completion.h>

struct completion my_completion;

// 初始化
init_completion(&my_completion);

// 等待完成
wait_for_completion(&my_completion);

// 通知完成
complete(&my_completion);      // 唤醒一个等待者
complete_all(&my_completion);  // 唤醒所有等待者

2.5 读写信号量

#include <linux/rwsem.h>

struct rw_semaphore my_rwsem;

// 初始化
init_rwsem(&my_rwsem);

// 读者
down_read(&my_rwsem);
// 读取临界区
up_read(&my_rwsem);

// 写者
down_write(&my_rwsem);
// 写入临界区
up_write(&my_rwsem);

3. 实际应用示例

3.1 设备驱动中的并发控制

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/spinlock.h>
#include <linux/semaphore.h>

struct my_device {
    struct cdev cdev;
    atomic_t open_count;
    spinlock_t data_lock;
    struct mutex io_mutex;
    struct semaphore sem;
    char *buffer;
    size_t buffer_size;
};

static struct my_device my_dev;

static int my_open(struct inode *inode, struct file *filp)
{
    // 使用原子操作计数
    if (atomic_inc_return(&my_dev.open_count) > 1) {
        atomic_dec(&my_dev.open_count);
        return -EBUSY;
    }
    
    filp->private_data = &my_dev;
    return 0;
}

static int my_release(struct inode *inode, struct file *filp)
{
    atomic_dec(&my_dev.open_count);
    return 0;
}

static ssize_t my_read(struct file *filp, char __user *buf, 
                      size_t count, loff_t *f_pos)
{
    struct my_device *dev = filp->private_data;
    unsigned long flags;
    ssize_t ret;
    
    // 使用互斥锁保护IO操作
    if (mutex_lock_interruptible(&dev->io_mutex))
        return -ERESTARTSYS;
    
    // 使用自旋锁保护共享数据
    spin_lock_irqsave(&dev->data_lock, flags);
    
    // 读取数据到用户空间
    if (count > dev->buffer_size - *f_pos)
        count = dev->buffer_size - *f_pos;
    
    if (copy_to_user(buf, dev->buffer + *f_pos, count)) {
        ret = -EFAULT;
    } else {
        *f_pos += count;
        ret = count;
    }
    
    spin_unlock_irqrestore(&dev->data_lock, flags);
    mutex_unlock(&dev->io_mutex);
    
    return ret;
}

static ssize_t my_write(struct file *filp, const char __user *buf,
                       size_t count, loff_t *f_pos)
{
    struct my_device *dev = filp->private_data;
    unsigned long flags;
    ssize_t ret;
    
    // 使用信号量限制并发写入者数量
    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;
    
    // 使用互斥锁和自旋锁
    mutex_lock(&dev->io_mutex);
    spin_lock_irqsave(&dev->data_lock, flags);
    
    if (copy_from_user(dev->buffer + *f_pos, buf, count)) {
        ret = -EFAULT;
    } else {
        *f_pos += count;
        ret = count;
    }
    
    spin_unlock_irqrestore(&dev->data_lock, flags);
    mutex_unlock(&dev->io_mutex);
    up(&dev->sem);
    
    return ret;
}

3.2 中断处理中的并发

#include <linux/interrupt.h>

struct my_device_data {
    spinlock_t lock;
    struct work_struct work;
    struct workqueue_struct *wq;
    void __iomem *reg_base;
};

static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    struct my_device_data *dev = dev_id;
    unsigned long flags;
    
    // 在中断处理中使用自旋锁
    spin_lock_irqsave(&dev->lock, flags);
    
    // 读取中断状态
    // 清除中断标志
    // 处理紧急事务
    
    spin_unlock_irqrestore(&dev->lock, flags);
    
    // 调度底半部处理
    queue_work(dev->wq, &dev->work);
    
    return IRQ_HANDLED;
}

// 底半部工作函数
static void my_work_handler(struct work_struct *work)
{
    struct my_device_data *dev = container_of(work, 
                                struct my_device_data, work);
    
    // 这里可以使用睡眠操作
    // 处理耗时任务
}

4. 选择正确的锁机制

4.1 锁选择指南

场景 推荐机制 原因
短期保护,中断上下文 自旋锁 不会睡眠,响应快
长期保护,进程上下文 互斥锁 可睡眠,不浪费CPU
多读少写 读写锁/信号量 提高并发性
简单计数 原子操作 无锁,高效
任务同步 完成量 专门用于同步

4.2 最佳实践

// 1. 按固定顺序获取锁,避免死锁
void correct_order(void)
{
    mutex_lock(&lock_a);
    mutex_lock(&lock_b);
    // 临界区
    mutex_unlock(&lock_b);
    mutex_unlock(&lock_a);
}

// 2. 避免在持有锁时调用可能睡眠的函数
void bad_practice(struct mutex *lock)
{
    mutex_lock(lock);
    copy_to_user(...);  // 可能睡眠
    mutex_unlock(lock); // 危险!
}

// 3. 使用锁的适当变体
int good_practice(struct mutex *lock)
{
    if (mutex_lock_interruptible(lock)) // 可被信号中断
        return -ERESTARTSYS;
    
    // 临界区
    mutex_unlock(lock);
    return 0;
}

5. 调试和检测

5.1 锁调试功能

// 配置内核选项
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_ATOMIC_SLEEP=y

// 锁统计信息
CONFIG_LOCK_STAT=y

5.2 死锁检测

// 使用lockdep内核功能
#include <linux/lockdep.h>

// 在初始化时注册锁
lockdep_set_class(&my_lock, &my_lock_key);

// 检查锁的使用情况
spin_lock_nested(&my_lock, SINGLE_DEPTH_NESTING);

正确理解和使用Linux内核的并发控制机制是编写稳定、高效驱动程序的关键。应根据具体场景选择合适的同步机制,并遵循最佳实践来避免竞态条件和死锁。


3.Linux等待队列

1. 等待队列概述

等待队列是Linux内核中用于实现进程睡眠和唤醒的机制,主要用于:

  • 进程在等待特定条件时进入睡眠状态
  • 当条件满足时唤醒等待的进程
  • 实现高效的同步和事件等待

2. 等待队列基本操作

2.1 定义和初始化

#include <linux/wait.h>
#include <linux/sched.h>

// 静态定义和初始化
DECLARE_WAIT_QUEUE_HEAD(my_wq);

// 动态初始化
wait_queue_head_t my_wq;
init_waitqueue_head(&my_wq);

// 定义等待队列条目
DECLARE_WAITQUEUE(wait, current);

2.2 基本睡眠函数

// 不可中断睡眠(通常不建议使用)
wait_event(wq, condition);
wait_event_timeout(wq, condition, timeout);

// 可中断睡眠(推荐使用)
wait_event_interruptible(wq, condition);
wait_event_interruptible_timeout(wq, condition, timeout);

// 可终止睡眠
wait_event_killable(wq, condition);
wait_event_killable_timeout(wq, condition, timeout);

2.3 唤醒函数

// 唤醒一个进程
wake_up(wq_head);
wake_up_interruptible(wq_head);

// 唤醒所有进程
wake_up_all(wq_head);
wake_up_interruptible_all(wq_head);

// 唤醒一个进程并同步
wake_up_sync(wq_head);
wake_up_interruptible_sync(wq_head);

3. 等待队列使用模式

3.1 基本使用示例

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>

static DECLARE_WAIT_QUEUE_HEAD(data_wq);
static int data_ready = 0;
static char shared_data[256];

// 生产者:产生数据后唤醒消费者
static void producer_function(const char *data)
{
    // 生产数据
    strncpy(shared_data, data, sizeof(shared_data) - 1);
    shared_data[sizeof(shared_data) - 1] = '\0';
    
    // 设置条件变量
    data_ready = 1;
    
    // 唤醒等待队列中的所有进程
    wake_up_interruptible(&data_wq);
    
    printk(KERN_INFO "Producer: data ready, waking up consumers\n");
}

// 消费者:等待数据可用
static ssize_t consumer_function(struct file *filp, char __user *buf, size_t count)
{
    int ret;
    
    // 等待数据就绪(可中断睡眠)
    ret = wait_event_interruptible(data_wq, data_ready != 0);
    if (ret) {
        // 被信号中断
        return -ERESTARTSYS;
    }
    
    // 数据已就绪,进行处理
    if (copy_to_user(buf, shared_data, min(count, sizeof(shared_data)))) {
        return -EFAULT;
    }
    
    // 重置条件
    data_ready = 0;
    
    return min(count, sizeof(shared_data));
}

3.2 手动等待队列操作

static ssize_t advanced_wait_example(struct file *filp, char __user *buf, size_t count)
{
    wait_queue_entry_t wait;
    int ret = 0;
    
    // 初始化等待队列条目
    init_waitqueue_entry(&wait, current);
    
    // 添加到等待队列
    add_wait_queue(&data_wq, &wait);
    
    // 设置进程状态
    set_current_state(TASK_INTERRUPTIBLE);
    
    while (!data_ready) {
        // 检查是否有信号 pending
        if (signal_pending(current)) {
            ret = -ERESTARTSYS;
            break;
        }
        
        // 调度其他进程运行
        schedule();
        set_current_state(TASK_INTERRUPTIBLE);
    }
    
    // 恢复运行状态
    set_current_state(TASK_RUNNING);
    
    // 从等待队列移除
    remove_wait_queue(&data_wq, &wait);
    
    if (!ret) {
        // 处理数据
        if (copy_to_user(buf, shared_data, min(count, sizeof(shared_data)))) {
            ret = -EFAULT;
        } else {
            ret = min(count, sizeof(shared_data));
            data_ready = 0;
        }
    }
    
    return ret;
}

4. 完整驱动示例

4.1 简单的字符设备驱动

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#define DEVICE_NAME "waitq_example"
#define BUFFER_SIZE 1024

struct waitq_device {
    struct cdev cdev;
    dev_t devno;
    wait_queue_head_t read_wq;
    wait_queue_head_t write_wq;
    char *buffer;
    size_t data_len;
    int read_avail;
    int write_avail;
    struct mutex lock;
};

static struct waitq_device *waitq_dev;

static int waitq_open(struct inode *inode, struct file *filp)
{
    struct waitq_device *dev = container_of(inode->i_cdev, 
                                          struct waitq_device, cdev);
    filp->private_data = dev;
    return 0;
}

static int waitq_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t waitq_read(struct file *filp, char __user *buf, 
                         size_t count, loff_t *f_pos)
{
    struct waitq_device *dev = filp->private_data;
    ssize_t ret;
    
    // 等待数据可读
    if (wait_event_interruptible(dev->read_wq, dev->read_avail)) {
        return -ERESTARTSYS;
    }
    
    // 保护共享数据
    mutex_lock(&dev->lock);
    
    // 读取数据
    count = min(count, dev->data_len);
    if (copy_to_user(buf, dev->buffer, count)) {
        ret = -EFAULT;
    } else {
        ret = count;
        dev->data_len = 0;
        dev->read_avail = 0;
        dev->write_avail = 1;
        
        // 唤醒可能的写者
        wake_up_interruptible(&dev->write_wq);
    }
    
    mutex_unlock(&dev->lock);
    return ret;
}

static ssize_t waitq_write(struct file *filp, const char __user *buf,
                          size_t count, loff_t *f_pos)
{
    struct waitq_device *dev = filp->private_data;
    ssize_t ret;
    
    count = min(count, (size_t)BUFFER_SIZE);
    
    // 等待缓冲区可写
    if (wait_event_interruptible(dev->write_wq, dev->write_avail)) {
        return -ERESTARTSYS;
    }
    
    mutex_lock(&dev->lock);
    
    // 写入数据
    if (copy_from_user(dev->buffer, buf, count)) {
        ret = -EFAULT;
    } else {
        dev->data_len = count;
        dev->read_avail = 1;
        dev->write_avail = 0;
        ret = count;
        
        // 唤醒读者
        wake_up_interruptible(&dev->read_wq);
    }
    
    mutex_unlock(&dev->lock);
    return ret;
}

static const struct file_operations waitq_fops = {
    .owner = THIS_MODULE,
    .open = waitq_open,
    .release = waitq_release,
    .read = waitq_read,
    .write = waitq_write,
};

static int __init waitq_init(void)
{
    int ret;
    
    // 分配设备结构
    waitq_dev = kzalloc(sizeof(*waitq_dev), GFP_KERNEL);
    if (!waitq_dev)
        return -ENOMEM;
    
    // 分配缓冲区
    waitq_dev->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
    if (!waitq_dev->buffer) {
        ret = -ENOMEM;
        goto free_dev;
    }
    
    // 初始化等待队列
    init_waitqueue_head(&waitq_dev->read_wq);
    init_waitqueue_head(&waitq_dev->write_wq);
    
    // 初始化互斥锁
    mutex_init(&waitq_dev->lock);
    
    // 初始状态:可写,不可读
    waitq_dev->write_avail = 1;
    waitq_dev->read_avail = 0;
    waitq_dev->data_len = 0;
    
    // 分配设备号
    ret = alloc_chrdev_region(&waitq_dev->devno, 0, 1, DEVICE_NAME);
    if (ret < 0)
        goto free_buffer;
    
    // 初始化cdev
    cdev_init(&waitq_dev->cdev, &waitq_fops);
    waitq_dev->cdev.owner = THIS_MODULE;
    
    // 添加cdev到系统
    ret = cdev_add(&waitq_dev->cdev, waitq_dev->devno, 1);
    if (ret)
        goto unregister_dev;
    
    printk(KERN_INFO "Wait queue example driver loaded\n");
    return 0;

unregister_dev:
    unregister_chrdev_region(waitq_dev->devno, 1);
free_buffer:
    kfree(waitq_dev->buffer);
free_dev:
    kfree(waitq_dev);
    return ret;
}

static void __exit waitq_exit(void)
{
    cdev_del(&waitq_dev->cdev);
    unregister_chrdev_region(waitq_dev->devno, 1);
    kfree(waitq_dev->buffer);
    kfree(waitq_dev);
    printk(KERN_INFO "Wait queue example driver unloaded\n");
}

module_init(waitq_init);
module_exit(waitq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Wait queue example driver");

5. 高级用法和模式

5.1 带超时的等待

static ssize_t wait_with_timeout(struct file *filp, char __user *buf, 
                                size_t count, loff_t *f_pos)
{
    struct my_device *dev = filp->private_data;
    long timeout_ret;
    
    // 等待最多2秒钟
    timeout_ret = wait_event_interruptible_timeout(dev->wq, 
                    dev->data_ready, 
                    msecs_to_jiffies(2000));
    
    if (timeout_ret == 0) {
        // 超时
        return -ETIMEDOUT;
    } else if (timeout_ret < 0) {
        // 被信号中断
        return -ERESTARTSYS;
    }
    
    // 条件满足,处理数据
    return process_data(dev, buf, count);
}

5.2 独占等待

static ssize_t exclusive_wait(struct file *filp, char __user *buf,
                             size_t count, loff_t *f_pos)
{
    wait_queue_entry_t wait;
    int ret = 0;
    
    init_waitqueue_entry(&wait, current);
    wait.flags |= WQ_FLAG_EXCLUSIVE;  // 设置为独占等待
    
    add_wait_queue(&my_wq, &wait);
    set_current_state(TASK_INTERRUPTIBLE);
    
    while (!condition) {
        if (signal_pending(current)) {
            ret = -ERESTARTSYS;
            break;
        }
        schedule();
        set_current_state(TASK_INTERRUPTIBLE);
    }
    
    set_current_state(TASK_RUNNING);
    remove_wait_queue(&my_wq, &wait);
    
    return ret;
}

5.3 轮询结合等待队列

static unsigned int waitq_poll(struct file *filp, poll_table *wait)
{
    struct waitq_device *dev = filp->private_data;
    unsigned int mask = 0;
    
    poll_wait(filp, &dev->read_wq, wait);
    poll_wait(filp, &dev->write_wq, wait);
    
    mutex_lock(&dev->lock);
    
    if (dev->read_avail)
        mask |= POLLIN | POLLRDNORM;  // 可读
    
    if (dev->write_avail)
        mask |= POLLOUT | POLLWRNORM; // 可写
    
    mutex_unlock(&dev->lock);
    
    return mask;
}

6. 最佳实践和注意事项

6.1 使用准则

  1. 总是使用可中断版本:除非有特殊需求,否则使用_interruptible版本
  2. 保护共享数据:等待队列通常需要与锁配合使用
  3. 条件检查:在睡眠前和唤醒后都要检查条件
  4. 避免虚假唤醒:使用循环检查条件

6.2 常见错误

// 错误:没有在循环中检查条件
wait_event_interruptible(wq, condition);  // 可能虚假唤醒

// 正确:手动实现循环检查
while (!condition) {
    wait_event_interruptible(wq, condition);
}

// 错误:忘记恢复进程状态
set_current_state(TASK_INTERRUPTIBLE);
schedule();
// 忘记: set_current_state(TASK_RUNNING);

// 错误:在持有锁时睡眠
mutex_lock(&lock);
wait_event_interruptible(wq, condition);  // 可能死锁!
mutex_unlock(&lock);

6.3 性能考虑

  1. 选择合适的唤醒函数

    • wake_up() - 唤醒所有等待者
    • wake_up_interruptible() - 只唤醒可中断等待者
    • wake_up_sync() - 同步唤醒,不重新调度
  2. 避免惊群效应:使用独占等待标志WQ_FLAG_EXCLUSIVE

等待队列是Linux内核中实现进程同步的核心机制,正确使用可以创建高效、响应及时的驱动程序。


4.IO多路复用

1. IO多路复用概述

IO多路复用是一种同时监控多个文件描述符的机制,允许单个进程处理多个IO操作。主要解决高并发场景下的IO效率问题。

1.1 核心概念

  • 文件描述符(File Descriptor): Linux中一切皆文件,socket、设备等都通过fd访问
  • 就绪通知(Ready Notification): 当fd可读、可写或出现异常时通知应用程序
  • 事件驱动(Event-Driven): 基于事件触发而非轮询

1.2 主要技术

  • select: 最古老的多路复用机制
  • poll: select的改进版本
  • epoll: Linux特有的高性能多路复用

2. select系统调用

2.1 select基本原理

#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

// 相关操作宏
void FD_ZERO(fd_set *set);           // 清空集合
void FD_SET(int fd, fd_set *set);    // 添加fd到集合
void FD_CLR(int fd, fd_set *set);    // 从集合移除fd
int FD_ISSET(int fd, fd_set *set);   // 检查fd是否在集合中

2.2 select使用示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#define MAX_FD 1024

int main() {
    fd_set readfds;
    struct timeval timeout;
    int ret;
    
    while (1) {
        // 清空文件描述符集合
        FD_ZERO(&readfds);
        
        // 添加标准输入到监控集合
        FD_SET(STDIN_FILENO, &readfds);
        
        // 设置超时时间为5秒
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        
        // 调用select
        ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &timeout);
        
        if (ret == -1) {
            perror("select");
            break;
        } else if (ret == 0) {
            printf("Timeout occurred! No data after 5 seconds.\n");
        } else {
            // 检查标准输入是否可读
            if (FD_ISSET(STDIN_FILENO, &readfds)) {
                char buf[1024];
                ssize_t n = read(STDIN_FILENO, buf, sizeof(buf) - 1);
                if (n > 0) {
                    buf[n] = '\0';
                    printf("Read from stdin: %s", buf);
                }
            }
        }
    }
    
    return 0;
}

2.3 select的局限性

  • 文件描述符数量限制(FD_SETSIZE,通常1024)
  • 每次调用需要重新设置fd_set
  • 线性扫描所有fd,效率随fd数量增加而下降

3. poll系统调用

3.1 poll基本原理

#include <poll.h>

struct pollfd {
    int fd;           // 文件描述符
    short events;     // 请求的事件
    short revents;    // 返回的事件
};

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

// 事件标志
#define POLLIN      0x001   // 有数据可读
#define POLLPRI     0x002   // 有紧急数据可读
#define POLLOUT     0x004   // 可写,不会阻塞
#define POLLERR     0x008   // 发生错误
#define POLLHUP     0x010   // 挂起
#define POLLNVAL    0x020   // 无效的fd

3.2 poll使用示例

#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <unistd.h>
#include <string.h>

#define MAX_FDS 10

int main() {
    struct pollfd fds[2];
    int timeout = 5000; // 5秒超时
    int ret;
    
    // 监控标准输入
    fds[0].fd = STDIN_FILENO;
    fds[0].events = POLLIN;
    
    // 监控标准输出(通常总是可写)
    fds[1].fd = STDOUT_FILENO;
    fds[1].events = POLLOUT;
    
    while (1) {
        ret = poll(fds, 2, timeout);
        
        if (ret == -1) {
            perror("poll");
            break;
        } else if (ret == 0) {
            printf("Poll timeout after 5 seconds.\n");
        } else {
            // 检查每个文件描述符
            for (int i = 0; i < 2; i++) {
                if (fds[i].revents & POLLIN) {
                    printf("fd %d is ready for reading\n", fds[i].fd);
                    char buf[1024];
                    ssize_t n = read(fds[i].fd, buf, sizeof(buf) - 1);
                    if (n > 0) {
                        buf[n] = '\0';
                        printf("Read: %s", buf);
                    }
                }
                
                if (fds[i].revents & POLLOUT) {
                    printf("fd %d is ready for writing\n", fds[i].fd);
                }
                
                if (fds[i].revents & POLLERR) {
                    printf("Error on fd %d\n", fds[i].fd);
                }
            }
        }
    }
    
    return 0;
}

4. epoll系统调用

4.1 epoll基本原理

epoll是Linux特有的高性能IO多路复用机制,解决了select/poll的性能问题。

#include <sys/epoll.h>

// 创建epoll实例
int epoll_create(int size);
int epoll_create1(int flags);

// 控制epoll事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

// 等待事件
int epoll_wait(int epfd, struct epoll_event *events,
               int maxevents, int timeout);

struct epoll_event {
    uint32_t events;    // epoll事件
    epoll_data_t data;  // 用户数据
};

typedef union epoll_data {
    void *ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

// 事件标志
#define EPOLLIN     0x001   // 可读
#define EPOLLOUT    0x004   // 可写
#define EPOLLET     0x800   // 边缘触发
#define EPOLLONESHOT 0x4000000 // 一次性事件

4.2 epoll使用示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define MAX_EVENTS 10

int main() {
    int epoll_fd;
    struct epoll_event event, events[MAX_EVENTS];
    int ret;
    
    // 创建epoll实例
    epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }
    
    // 添加标准输入到epoll监控
    event.events = EPOLLIN;
    event.data.fd = STDIN_FILENO;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &event) == -1) {
        perror("epoll_ctl");
        close(epoll_fd);
        exit(EXIT_FAILURE);
    }
    
    printf("Epoll started, monitoring stdin...\n");
    
    while (1) {
        // 等待事件,超时5秒
        ret = epoll_wait(epoll_fd, events, MAX_EVENTS, 5000);
        
        if (ret == -1) {
            perror("epoll_wait");
            break;
        } else if (ret == 0) {
            printf("No events within 5 seconds.\n");
            continue;
        }
        
        // 处理所有就绪的事件
        for (int i = 0; i < ret; i++) {
            if (events[i].events & EPOLLIN) {
                printf("File descriptor %d is ready for reading\n", 
                       events[i].data.fd);
                
                char buf[1024];
                ssize_t n = read(events[i].data.fd, buf, sizeof(buf) - 1);
                if (n > 0) {
                    buf[n] = '\0';
                    printf("Read %zd bytes: %s", n, buf);
                    
                    // 如果是"quit"则退出
                    if (strncmp(buf, "quit", 4) == 0) {
                        printf("Exiting...\n");
                        close(epoll_fd);
                        return 0;
                    }
                } else if (n == 0) {
                    printf("File descriptor %d closed\n", events[i].data.fd);
                    // 从epoll中移除
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                }
            }
            
            if (events[i].events & EPOLLERR) {
                printf("Error on file descriptor %d\n", events[i].data.fd);
            }
        }
    }
    
    close(epoll_fd);
    return 0;
}

4.3 水平触发 vs 边缘触发

// 水平触发(默认)
event.events = EPOLLIN;  // 水平触发

// 边缘触发
event.events = EPOLLIN | EPOLLET;  // 边缘触发

水平触发(Level-Triggered):

  • 只要文件描述符可读/可写,就会持续通知
  • 类似于select/poll的行为
  • 编程更简单,不容易丢失事件

边缘触发(Edge-Triggered):

  • 只有当文件描述符状态发生变化时通知
  • 需要一次性读取所有可用数据
  • 性能更好,但编程更复杂

4.4 边缘触发示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

#define MAX_EVENTS 10
#define BUFFER_SIZE 1024

// 设置文件描述符为非阻塞
int set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) return -1;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int main() {
    int epoll_fd;
    struct epoll_event event, events[MAX_EVENTS];
    int ret;
    
    epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }
    
    // 设置标准输入为非阻塞
    if (set_nonblocking(STDIN_FILENO) == -1) {
        perror("set_nonblocking");
        close(epoll_fd);
        exit(EXIT_FAILURE);
    }
    
    // 使用边缘触发
    event.events = EPOLLIN | EPOLLET;
    event.data.fd = STDIN_FILENO;
    
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &event) == -1) {
        perror("epoll_ctl");
        close(epoll_fd);
        exit(EXIT_FAILURE);
    }
    
    printf("Epoll ET mode started...\n");
    
    while (1) {
        ret = epoll_wait(epoll_fd, events, MAX_EVENTS, 5000);
        
        if (ret == -1) {
            perror("epoll_wait");
            break;
        } else if (ret == 0) {
            printf("Timeout...\n");
            continue;
        }
        
        for (int i = 0; i < ret; i++) {
            if (events[i].events & EPOLLIN) {
                printf("EPOLLIN event on fd %d\n", events[i].data.fd);
                
                // 边缘触发需要读取所有可用数据
                while (1) {
                    char buf[BUFFER_SIZE];
                    ssize_t n = read(events[i].data.fd, buf, sizeof(buf) - 1);
                    
                    if (n > 0) {
                        buf[n] = '\0';
                        printf("Read %zd bytes: %s", n, buf);
                    } else if (n == -1) {
                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
                            // 没有更多数据可读
                            printf("No more data to read\n");
                            break;
                        } else {
                            perror("read");
                            break;
                        }
                    } else { // n == 0
                        printf("File descriptor closed\n");
                        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                        break;
                    }
                }
            }
        }
    }
    
    close(epoll_fd);
    return 0;
}

5. 三种机制对比

特性 select poll epoll
性能 O(n) O(n) O(1)
最大fd数 FD_SETSIZE(1024) 无限制 无限制
工作效率 随fd增加下降 随fd增加下降 高效
内存使用 固定大小 动态分配 动态分配
触发方式 水平触发 水平触发 水平/边缘触发
内核通知 每次重建fd_set 每次重建pollfd数组 事件驱动

6. 实际应用:简单的TCP服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <errno.h>

#define MAX_EVENTS 64
#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int server_fd, epoll_fd;
    struct sockaddr_in address;
    struct epoll_event event, events[MAX_EVENTS];
    int opt = 1;
    int addrlen = sizeof(address);
    
    // 创建服务器socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    
    // 设置socket选项
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 
                   &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    
    // 绑定socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    
    // 开始监听
    if (listen(server_fd, 10) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    printf("Server listening on port %d\n", PORT);
    
    // 创建epoll实例
    epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }
    
    // 添加服务器socket到epoll
    event.events = EPOLLIN;
    event.data.fd = server_fd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {
        perror("epoll_ctl: server_fd");
        exit(EXIT_FAILURE);
    }
    
    while (1) {
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (nfds == -1) {
            perror("epoll_wait");
            break;
        }
        
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == server_fd) {
                // 新的客户端连接
                int client_fd = accept(server_fd, 
                                     (struct sockaddr *)&address, 
                                     (socklen_t*)&addrlen);
                if (client_fd < 0) {
                    perror("accept");
                    continue;
                }
                
                printf("New client connected: fd=%d\n", client_fd);
                
                // 添加客户端socket到epoll
                event.events = EPOLLIN | EPOLLET; // 边缘触发
                event.data.fd = client_fd;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) {
                    perror("epoll_ctl: client_fd");
                    close(client_fd);
                }
            } else {
                // 客户端数据可读
                int client_fd = events[i].data.fd;
                char buffer[BUFFER_SIZE];
                
                while (1) {
                    ssize_t count = read(client_fd, buffer, sizeof(buffer) - 1);
                    
                    if (count > 0) {
                        buffer[count] = '\0';
                        printf("Received from client %d: %s", client_fd, buffer);
                        
                        // 回显数据
                        write(client_fd, buffer, count);
                    } else if (count == 0) {
                        // 客户端断开连接
                        printf("Client %d disconnected\n", client_fd);
                        close(client_fd);
                        break;
                    } else {
                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
                            // 没有更多数据
                            break;
                        } else {
                            perror("read");
                            close(client_fd);
                            break;
                        }
                    }
                }
            }
        }
    }
    
    close(server_fd);
    close(epoll_fd);
    return 0;
}

7. 最佳实践

  1. 选择合适的机制

    • 少量连接:select/poll
    • 大量连接:epoll
  2. 边缘触发注意事项

    • 必须使用非阻塞IO
    • 必须一次性读取所有数据
    • 性能更好但编程更复杂
  3. 错误处理

    • 总是检查系统调用返回值
    • 处理EAGAIN/EWOULDBLOCK错误
    • 及时关闭文件描述符
  4. 性能优化

    • 使用边缘触发减少系统调用
    • 合理设置缓冲区大小
    • 避免不必要的内存拷贝

IO多路复用是现代高性能网络编程的核心技术,正确使用可以显著提高程序的并发处理能力。


5.信号驱动IO

1. 信号驱动IO概述

信号驱动IO是一种异步IO模型,当文件描述符就绪时,内核通过信号通知应用程序,而不是让应用程序阻塞等待。

1.1 工作原理

  1. 应用程序启用文件描述符的信号驱动IO
  2. 应用程序继续执行其他任务(不阻塞)
  3. 当IO就绪时,内核发送SIGIO信号
  4. 信号处理函数执行实际的IO操作

1.2 适用场景

  • 需要异步处理IO的应用程序
  • 需要同时处理多个IO源
  • 实时性要求较高的应用
  • 不适合高性能服务器(信号处理开销较大)

2. 信号驱动IO设置

2.1 基本设置步骤

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

static int fd;

// SIGIO信号处理函数
void sigio_handler(int sig) {
    char buf[256];
    ssize_t n;
    
    printf("SIGIO received, fd is ready for IO\n");
    
    // 读取数据
    n = read(fd, buf, sizeof(buf) - 1);
    if (n > 0) {
        buf[n] = '\0';
        printf("Read %zd bytes: %s\n", n, buf);
    } else if (n == 0) {
        printf("End of file\n");
    } else {
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            perror("read");
        }
    }
}

int setup_sigio(int file_fd) {
    struct sigaction sa;
    int flags;
    
    fd = file_fd;
    
    // 设置信号处理函数
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigio_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0; // 不要使用SA_RESTART
    
    if (sigaction(SIGIO, &sa, NULL) == -1) {
        perror("sigaction");
        return -1;
    }
    
    // 设置文件描述符的属主,以便接收信号
    if (fcntl(fd, F_SETOWN, getpid()) == -1) {
        perror("fcntl F_SETOWN");
        return -1;
    }
    
    // 获取当前文件状态标志
    flags = fcntl(fd, F_GETFL);
    if (flags == -1) {
        perror("fcntl F_GETFL");
        return -1;
    }
    
    // 启用异步IO和non-blocking
    if (fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1) {
        perror("fcntl F_SETFL");
        return -1;
    }
    
    printf("Signal-driven IO setup complete for fd %d\n", fd);
    return 0;
}

2.2 完整示例:标准输入监控

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

static volatile sig_atomic_t got_signal = 0;
static int signal_fd;

void sigio_handler(int sig) {
    got_signal = 1;
    signal_fd = -1; // 在实际应用中需要确定是哪个fd
}

int main() {
    struct sigaction sa;
    int flags;
    int count = 0;
    
    printf("Signal-driven IO demo for stdin\n");
    printf("Process PID: %d\n", getpid());
    
    // 设置信号处理函数
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigio_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    
    if (sigaction(SIGIO, &sa, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }
    
    // 设置标准输入的信号驱动IO
    if (fcntl(STDIN_FILENO, F_SETOWN, getpid()) == -1) {
        perror("fcntl F_SETOWN");
        exit(EXIT_FAILURE);
    }
    
    flags = fcntl(STDIN_FILENO, F_GETFL);
    if (flags == -1) {
        perror("fcntl F_GETFL");
        exit(EXIT_FAILURE);
    }
    
    if (fcntl(STDIN_FILENO, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1) {
        perror("fcntl F_SETFL");
        exit(EXIT_FAILURE);
    }
    
    printf("Signal-driven IO is set up. Type something (or 'quit' to exit):\n");
    
    while (1) {
        // 主循环可以做其他工作
        printf("Working... (%d)\n", count++);
        sleep(2);
        
        // 检查是否有信号到达
        if (got_signal) {
            char buf[256];
            ssize_t n;
            
            printf("Processing SIGIO...\n");
            
            // 读取所有可用数据
            while ((n = read(STDIN_FILENO, buf, sizeof(buf) - 1)) > 0) {
                buf[n] = '\0';
                printf("Read: %s", buf);
                
                // 检查退出条件
                if (strncmp(buf, "quit", 4) == 0) {
                    printf("Exiting...\n");
                    exit(EXIT_SUCCESS);
                }
            }
            
            if (n == -1) {
                if (errno != EAGAIN && errno != EWOULDBLOCK) {
                    perror("read");
                }
            }
            
            got_signal = 0;
        }
    }
    
    return 0;
}

3. 高级用法:多文件描述符处理

3.1 使用实时信号

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

#define MAX_FDS 10

typedef struct {
    int fd;
    char name[32];
} fd_info_t;

static fd_info_t fd_list[MAX_FDS];
static int fd_count = 0;

// 使用实时信号可以携带更多信息
void sigrtmin_handler(int sig, siginfo_t *info, void *context) {
    int fd = info->si_fd;  // 触发信号的文件描述符
    
    printf("Real-time signal received for fd: %d\n", fd);
    
    // 查找对应的文件描述符信息
    for (int i = 0; i < fd_count; i++) {
        if (fd_list[i].fd == fd) {
            printf("IO ready on %s (fd=%d)\n", fd_list[i].name, fd);
            break;
        }
    }
}

int setup_sigio_rt(int fd, const char *name) {
    struct sigaction sa;
    int flags;
    
    if (fd_count >= MAX_FDS) {
        fprintf(stderr, "Too many file descriptors\n");
        return -1;
    }
    
    // 存储文件描述符信息
    fd_list[fd_count].fd = fd;
    strncpy(fd_list[fd_count].name, name, sizeof(fd_list[fd_count].name) - 1);
    fd_count++;
    
    // 设置实时信号处理(第一次调用时设置)
    if (fd_count == 1) {
        memset(&sa, 0, sizeof(sa));
        sa.sa_sigaction = sigrtmin_handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_SIGINFO;  // 使用sa_sigaction
        
        if (sigaction(SIGRTMIN, &sa, NULL) == -1) {
            perror("sigaction SIGRTMIN");
            return -1;
        }
    }
    
    // 设置文件描述符属性
    if (fcntl(fd, F_SETSIG, SIGRTMIN) == -1) {
        perror("fcntl F_SETSIG");
        return -1;
    }
    
    if (fcntl(fd, F_SETOWN, getpid()) == -1) {
        perror("fcntl F_SETOWN");
        return -1;
    }
    
    flags = fcntl(fd, F_GETFL);
    if (flags == -1) {
        perror("fcntl F_GETFL");
        return -1;
    }
    
    if (fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1) {
        perror("fcntl F_SETFL");
        return -1;
    }
    
    printf("Real-time signal IO setup for %s (fd=%d)\n", name, fd);
    return 0;
}

3.2 套接字信号驱动IO

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

static int server_fd;
static volatile sig_atomic_t data_available = 0;

void sigio_handler(int sig) {
    data_available = 1;
}

void setup_socket_sigio(int sock_fd) {
    struct sigaction sa;
    int flags;
    
    // 设置信号处理
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigio_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    
    if (sigaction(SIGIO, &sa, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }
    
    // 设置套接字属性
    if (fcntl(sock_fd, F_SETOWN, getpid()) == -1) {
        perror("fcntl F_SETOWN");
        exit(EXIT_FAILURE);
    }
    
    flags = fcntl(sock_fd, F_GETFL);
    if (flags == -1) {
        perror("fcntl F_GETFL");
        exit(EXIT_FAILURE);
    }
    
    if (fcntl(sock_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1) {
        perror("fcntl F_SETFL");
        exit(EXIT_FAILURE);
    }
}

int main() {
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    int opt = 1;
    
    // 创建服务器套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    
    // 设置套接字选项
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 
                   &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    
    // 绑定和监听
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    printf("Server listening on port %d\n", PORT);
    printf("Process PID: %d\n", getpid());
    
    // 设置信号驱动IO
    setup_socket_sigio(server_fd);
    
    while (1) {
        // 主循环可以处理其他任务
        printf("Server is doing other work...\n");
        sleep(3);
        
        if (data_available) {
            printf("Handling incoming connection...\n");
            
            int client_fd;
            struct sockaddr_in client_addr;
            socklen_t client_len = sizeof(client_addr);
            char buffer[BUFFER_SIZE];
            
            // 接受连接
            client_fd = accept(server_fd, (struct sockaddr *)&client_addr, 
                             &client_len);
            if (client_fd >= 0) {
                char client_ip[INET_ADDRSTRLEN];
                inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
                printf("Accepted connection from %s:%d\n", 
                       client_ip, ntohs(client_addr.sin_port));
                
                // 读取客户端数据
                ssize_t n = read(client_fd, buffer, sizeof(buffer) - 1);
                if (n > 0) {
                    buffer[n] = '\0';
                    printf("Received: %s\n", buffer);
                    
                    // 发送响应
                    const char *response = "Hello from signal-driven server!\n";
                    write(client_fd, response, strlen(response));
                }
                
                close(client_fd);
                printf("Connection closed\n");
            } else {
                if (errno != EAGAIN && errno != EWOULDBLOCK) {
                    perror("accept");
                }
            }
            
            data_available = 0;
        }
    }
    
    close(server_fd);
    return 0;
}

4. 信号驱动IO的限制和问题

4.1 常见问题及解决方案

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

// 问题1:信号丢失
// 解决方案:使用信号队列或实时信号

// 问题2:多个文件描述符的信号区分
// 解决方案1:在信号处理函数中检查所有fd
void sigio_handler_check_all(int sig) {
    printf("SIGIO received, checking all monitored fds...\n");
    
    // 这里需要维护一个被监控的fd列表
    // 检查每个fd的IO状态
}

// 解决方案2:使用不同的信号
void setup_multiple_signals() {
    struct sigaction sa;
    
    // 为不同的fd设置不同的信号
    // 但这受限于可用信号数量
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigio_handler_check_all;
    sigemptyset(&sa.sa_mask);
    
    // 可以绑定多个信号到同一个处理函数
    sigaction(SIGIO, &sa, NULL);
    sigaction(SIGURG, &sa, NULL);  // 用于带外数据
}

// 问题3:可移植性
// 不同Unix系统对信号驱动IO的支持不同
void check_system_support() {
#ifdef O_ASYNC
    printf("O_ASYNC is supported\n");
#else
    printf("O_ASYNC is NOT supported\n");
#endif

#ifdef F_SETOWN
    printf("F_SETOWN is supported\n");
#else
    printf("F_SETOWN is NOT supported\n");
#endif
}

// 问题4:性能考虑
// 信号处理的开销可能比轮询更大
void performance_considerations() {
    printf("Signal-driven IO performance notes:\n");
    printf("1. Signal handling has overhead\n");
    printf("2. Not suitable for high-rate IO\n");
    printf("3. Good for low-rate, responsive applications\n");
    printf("4. Consider epoll for high-performance servers\n");
}

5. 与其他IO模型对比

5.1 性能测试示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>

#define TEST_DURATION 5  // 测试5秒

static volatile int signal_count = 0;
static struct timeval start_time, end_time;

void test_sigio_handler(int sig) {
    signal_count++;
}

// 信号驱动IO性能测试
void test_signal_driven_io() {
    struct sigaction sa;
    int pipefd[2];
    int flags;
    char buffer[1024];
    
    // 创建管道用于测试
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return;
    }
    
    // 设置信号处理
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = test_sigio_handler;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGIO, &sa, NULL);
    
    // 设置写端为信号驱动
    flags = fcntl(pipefd[1], F_GETFL);
    fcntl(pipefd[1], F_SETFL, flags | O_ASYNC);
    fcntl(pipefd[1], F_SETOWN, getpid());
    
    printf("Starting signal-driven IO performance test...\n");
    
    gettimeofday(&start_time, NULL);
    signal_count = 0;
    
    // 持续写入数据触发信号
    int writes = 0;
    while (1) {
        struct timeval current_time;
        gettimeofday(&current_time, NULL);
        
        if (current_time.tv_sec - start_time.tv_sec >= TEST_DURATION) {
            break;
        }
        
        // 写入数据触发SIGIO
        if (write(pipefd[1], buffer, sizeof(buffer)) > 0) {
            writes++;
        }
        
        // 从读端读取数据,避免管道满
        read(pipefd[0], buffer, sizeof(buffer));
    }
    
    gettimeofday(&end_time, NULL);
    
    double elapsed = (end_time.tv_sec - start_time.tv_sec) + 
                    (end_time.tv_usec - start_time.tv_usec) / 1000000.0;
    
    printf("Signal-driven IO test results:\n");
    printf("  Duration: %.2f seconds\n", elapsed);
    printf("  Signals received: %d\n", signal_count);
    printf("  Writes performed: %d\n", writes);
    printf("  Signals per second: %.2f\n", signal_count / elapsed);
    
    close(pipefd[0]);
    close(pipefd[1]);
}

6. 最佳实践

6.1 使用准则

// 1. 总是使用非阻塞IO配合信号驱动IO
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK);

// 2. 在信号处理函数中做最少的工作
void efficient_handler(int sig) {
    // 只设置标志,在主循环中处理IO
    io_ready_flag = 1;
}

// 3. 使用实时信号避免信号丢失
fcntl(fd, F_SETSIG, SIGRTMIN);  // 使用实时信号

// 4. 正确处理EAGAIN/EWOULDBLOCK
ssize_t n = read(fd, buf, size);
if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
    // 没有数据可读,正常情况
}

// 5. 考虑信号队列限制
printf("Signal queue length: %ld\n", sysconf(_SC_SIGQUEUE_MAX));

6.2 错误处理

int robust_sigio_setup(int fd) {
    struct sigaction sa;
    int flags;
    
    // 检查文件描述符有效性
    if (fcntl(fd, F_GETFD) == -1) {
        perror("Invalid file descriptor");
        return -1;
    }
    
    // 设置信号处理
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigio_handler;
    sigemptyset(&sa.sa_mask);
    
    if (sigaction(SIGIO, &sa, NULL) == -1) {
        perror("sigaction failed");
        return -1;
    }
    
    // 设置文件属主
    if (fcntl(fd, F_SETOWN, getpid()) == -1) {
        perror("fcntl F_SETOWN failed");
        return -1;
    }
    
    // 启用异步IO
    flags = fcntl(fd, F_GETFL);
    if (flags == -1) {
        perror("fcntl F_GETFL failed");
        return -1;
    }
    
    if (fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1) {
        perror("fcntl F_SETFL failed");
        return -1;
    }
    
    return 0;
}

信号驱动IO提供了一种异步处理IO的方式,但在高性能场景下通常不如epoll高效。它适用于需要快速响应但IO频率不高的应用程序。


6.Linux定时器

1. Linux定时器概述

Linux提供了多种定时器机制,用于在特定时间点或周期性地执行任务。

1.1 定时器类型

  • 间隔定时器:setitimer,传统的UNIX定时器
  • POSIX定时器:timer_create,更灵活的定时器
  • 内核定时器:用于内核模块开发
  • 高精度定时器:hrtimer,纳秒级精度

2. 间隔定时器 (setitimer)

2.1 基本用法

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>

// 定时器信号处理函数
void timer_handler(int sig) {
    static int count = 0;
    printf("Timer expired %d times\n", ++count);
}

int main() {
    struct itimerval timer;
    struct sigaction sa;
    
    printf("Setting up interval timer...\n");
    printf("Process PID: %d\n", getpid());
    
    // 设置信号处理
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = timer_handler;
    sigaction(SIGALRM, &sa, NULL);
    
    // 配置定时器:首次1秒后触发,之后每2秒触发
    timer.it_value.tv_sec = 1;      // 第一次到期时间
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 2;   // 间隔时间
    timer.it_interval.tv_usec = 0;
    
    // 启动定时器
    if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {
        perror("setitimer");
        exit(EXIT_FAILURE);
    }
    
    printf("Timer started. First expiration in 1 second, then every 2 seconds.\n");
    
    // 主循环
    while (1) {
        pause();  // 等待信号
    }
    
    return 0;
}

2.2 三种定时器类型

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>

void real_timer_handler(int sig) {
    printf("REAL timer: wall clock time elapsed\n");
}

void virtual_timer_handler(int sig) {
    printf("VIRTUAL timer: process CPU time elapsed\n");
}

void prof_timer_handler(int sig) {
    printf("PROF timer: process CPU time (system + user) elapsed\n");
}

int main() {
    struct itimerval timer;
    struct sigaction sa;
    
    // 设置信号处理函数
    memset(&sa, 0, sizeof(sa));
    
    // 实时定时器(实际时间)
    sa.sa_handler = real_timer_handler;
    sigaction(SIGALRM, &sa, NULL);
    
    // 虚拟定时器(用户态CPU时间)
    sa.sa_handler = virtual_timer_handler;
    sigaction(SIGVTALRM, &sa, NULL);
    
    // 统计分析定时器(总CPU时间)
    sa.sa_handler = prof_timer_handler;
    sigaction(SIGPROF, &sa, NULL);
    
    // 设置实时定时器:每3秒
    timer.it_value.tv_sec = 3;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 3;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);
    
    // 设置虚拟定时器:每1秒用户CPU时间
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 1;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_VIRTUAL, &timer, NULL);
    
    // 设置统计分析定时器:每2秒总CPU时间
    timer.it_value.tv_sec = 2;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 2;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_PROF, &timer, NULL);
    
    printf("All timers started. Doing some work...\n");
    
    // 模拟一些CPU工作
    int i = 0;
    while (1) {
        // 用户态工作
        for (long j = 0; j < 1000000; j++) {
            i += j % 100;
        }
        
        // 系统调用(进入内核态)
        usleep(100000);  // 睡眠100ms
        
        if (i > 1000000000) break; // 防止溢出
    }
    
    return 0;
}

3. POSIX定时器

3.1 基本用法

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <string.h>

timer_t timer_id;

// 定时器通知处理函数
void timer_callback(union sigval val) {
    static int count = 0;
    int *user_data = (int *)val.sival_ptr;
    
    printf("POSIX Timer callback: count=%d, user_data=%d\n", 
           ++count, *user_data);
    
    // 修改用户数据
    (*user_data)++;
}

int main() {
    struct sigevent sev;
    struct itimerspec its;
    int user_data = 100;
    
    printf("Creating POSIX timer...\n");
    
    // 设置定时器事件
    memset(&sev, 0, sizeof(sev));
    sev.sigev_notify = SIGEV_THREAD;  // 创建新线程执行回调
    sev.sigev_value.sival_ptr = &user_data;  // 传递用户数据
    sev.sigev_notify_function = timer_callback;
    sev.sigev_notify_attributes = NULL;  // 使用默认线程属性
    
    // 创建定时器
    if (timer_create(CLOCK_REALTIME, &sev, &timer_id) == -1) {
        perror("timer_create");
        exit(EXIT_FAILURE);
    }
    
    // 设置定时器:首次1秒后,之后每2秒
    its.it_value.tv_sec = 1;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 2;
    its.it_interval.tv_nsec = 0;
    
    // 启动定时器
    if (timer_settime(timer_id, 0, &its, NULL) == -1) {
        perror("timer_settime");
        exit(EXIT_FAILURE);
    }
    
    printf("POSIX timer started. Running for 10 seconds...\n");
    
    // 运行一段时间
    sleep(10);
    
    // 删除定时器
    timer_delete(timer_id);
    printf("Timer deleted. Exiting.\n");
    
    return 0;
}

3.2 多种通知方式

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <string.h>

timer_t timer1, timer2, timer3;

// 信号处理函数
void signal_handler(int sig, siginfo_t *info, void *context) {
    printf("Signal %d received from timer, user_data=%d\n", 
           sig, *(int *)info->si_value.sival_ptr);
}

// 线程回调函数
void thread_callback(union sigval val) {
    printf("Thread callback: user_data=%d\n", *(int *)val.sival_ptr);
}

int main() {
    struct sigevent sev;
    struct itimerspec its;
    struct sigaction sa;
    int user_data1 = 100, user_data2 = 200, user_data3 = 300;
    
    printf("Testing different POSIX timer notifications...\n");
    
    // 方式1:信号通知
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = signal_handler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR1, &sa, NULL);
    
    memset(&sev, 0, sizeof(sev));
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIGUSR1;
    sev.sigev_value.sival_ptr = &user_data1;
    timer_create(CLOCK_REALTIME, &sev, &timer1);
    
    // 方式2:线程回调
    memset(&sev, 0, sizeof(sev));
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_value.sival_ptr = &user_data2;
    sev.sigev_notify_function = thread_callback;
    timer_create(CLOCK_REALTIME, &sev, &timer2);
    
    // 方式3:不通知(用于统计)
    memset(&sev, 0, sizeof(sev));
    sev.sigev_notify = SIGEV_NONE;
    timer_create(CLOCK_REALTIME, &sev, &timer3);
    
    // 启动定时器1:每3秒,信号通知
    its.it_value.tv_sec = 3;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 3;
    its.it_interval.tv_nsec = 0;
    timer_settime(timer1, 0, &its, NULL);
    
    // 启动定时器2:每5秒,线程回调
    its.it_value.tv_sec = 5;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 5;
    its.it_interval.tv_nsec = 0;
    timer_settime(timer2, 0, &its, NULL);
    
    printf("Timers started. Running for 20 seconds...\n");
    sleep(20);
    
    // 清理
    timer_delete(timer1);
    timer_delete(timer2);
    timer_delete(timer3);
    
    printf("All timers deleted.\n");
    return 0;
}

4. 高精度定时器 (hrtimer)

4.1 用户空间高精度定时器

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <string.h>

timer_t timer_id;

void high_res_handler(union sigval val) {
    struct timespec ts;
    static int count = 0;
    
    // 获取高精度时间
    clock_gettime(CLOCK_MONOTONIC, &ts);
    
    printf("High-res timer: count=%d, time=%.6f seconds\n",
           ++count, ts.tv_sec + ts.tv_nsec / 1e9);
}

int main() {
    struct sigevent sev;
    struct itimerspec its;
    
    printf("High-resolution timer test (nanosecond precision)\n");
    
    // 创建高精度定时器
    memset(&sev, 0, sizeof(sev));
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = high_res_handler;
    
    if (timer_create(CLOCK_MONOTONIC, &sev, &timer_id) == -1) {
        perror("timer_create CLOCK_MONOTONIC");
        exit(EXIT_FAILURE);
    }
    
    // 设置高精度定时:每100毫秒
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = 100000000;  // 100ms
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 100000000;  // 100ms
    
    if (timer_settime(timer_id, 0, &its, NULL) == -1) {
        perror("timer_settime");
        exit(EXIT_FAILURE);
    }
    
    printf("High-resolution timer started (100ms intervals). Running for 5 seconds...\n");
    sleep(5);
    
    // 获取定时器剩余时间
    struct itimerspec curr_its;
    timer_gettime(timer_id, &curr_its);
    printf("Remaining time: %ld.%09ld seconds\n", 
           curr_its.it_value.tv_sec, curr_its.it_value.tv_nsec);
    
    timer_delete(timer_id);
    printf("High-resolution timer deleted.\n");
    
    return 0;
}

5. 定时器在应用中的使用

5.1 超时处理示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#include <errno.h>

static volatile sig_atomic_t timeout_occurred = 0;

void timeout_handler(int sig) {
    timeout_occurred = 1;
    printf("Timeout! Operation took too long.\n");
}

// 带超时的读取操作
ssize_t read_with_timeout(int fd, void *buf, size_t count, int timeout_sec) {
    struct itimerval timer, old_timer;
    struct sigaction sa, old_sa;
    ssize_t n;
    
    // 设置超时处理
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = timeout_handler;
    sigaction(SIGALRM, &sa, &old_sa);
    
    // 设置定时器
    timer.it_value.tv_sec = timeout_sec;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 0;
    
    timeout_occurred = 0;
    setitimer(ITIMER_REAL, &timer, &old_timer);
    
    // 执行读取操作
    n = read(fd, buf, count);
    
    // 恢复原来的定时器和信号处理
    setitimer(ITIMER_REAL, &old_timer, NULL);
    sigaction(SIGALRM, &old_sa, NULL);
    
    if (timeout_occurred) {
        errno = ETIMEDOUT;
        return -1;
    }
    
    return n;
}

int main() {
    char buf[256];
    
    printf("Read with timeout example. You have 5 seconds to type something:\n");
    
    ssize_t n = read_with_timeout(STDIN_FILENO, buf, sizeof(buf) - 1, 5);
    
    if (n == -1) {
        if (errno == ETIMEDOUT) {
            printf("Read operation timed out.\n");
        } else {
            perror("read");
        }
    } else {
        buf[n] = '\0';
        printf("Successfully read: %s", buf);
    }
    
    return 0;
}

5.2 周期性任务调度

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>

typedef struct {
    int task_id;
    int execution_count;
    long total_execution_time;
} task_context_t;

timer_t timer_id;
task_context_t task_ctx = {1, 0, 0};

void periodic_task(union sigval val) {
    task_context_t *ctx = (task_context_t *)val.sival_ptr;
    struct timespec start, end;
    
    // 记录开始时间
    clock_gettime(CLOCK_MONOTONIC, &start);
    
    // 模拟任务执行
    printf("Task %d executing... (count=%d)\n", 
           ctx->task_id, ++ctx->execution_count);
    
    // 模拟一些工作
    for (volatile long i = 0; i < 1000000; i++) {
        // 空循环模拟工作
    }
    
    // 记录结束时间并计算执行时间
    clock_gettime(CLOCK_MONOTONIC, &end);
    long execution_time = (end.tv_sec - start.tv_sec) * 1000000 + 
                         (end.tv_nsec - start.tv_nsec) / 1000;
    
    ctx->total_execution_time += execution_time;
    
    printf("Task %d completed in %ld microseconds\n", 
           ctx->task_id, execution_time);
}

int main() {
    struct sigevent sev;
    struct itimerspec its;
    
    printf("Periodic task scheduler started.\n");
    
    // 创建定时器
    memset(&sev, 0, sizeof(sev));
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_value.sival_ptr = &task_ctx;
    sev.sigev_notify_function = periodic_task;
    
    if (timer_create(CLOCK_MONOTONIC, &sev, &timer_id) == -1) {
        perror("timer_create");
        exit(EXIT_FAILURE);
    }
    
    // 设置周期性执行:每1秒
    its.it_value.tv_sec = 1;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 1;
    its.it_interval.tv_nsec = 0;
    
    if (timer_settime(timer_id, 0, &its, NULL) == -1) {
        perror("timer_settime");
        exit(EXIT_FAILURE);
    }
    
    printf("Periodic task scheduled every 1 second. Running for 10 seconds...\n");
    sleep(10);
    
    // 停止定时器
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;
    timer_settime(timer_id, 0, &its, NULL);
    
    printf("\nTask statistics:\n");
    printf("  Executions: %d\n", task_ctx.execution_count);
    printf("  Total execution time: %ld microseconds\n", task_ctx.total_execution_time);
    printf("  Average execution time: %.2f microseconds\n", 
           (float)task_ctx.total_execution_time / task_ctx.execution_count);
    
    timer_delete(timer_id);
    printf("Scheduler stopped.\n");
    
    return 0;
}

6. 内核定时器示例

6.1 简单内核模块定时器

/*
 * 内核定时器示例模块
 * 需要root权限加载:insmod timer_module.ko
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>

static struct timer_list my_timer;
static int count = 0;

void timer_callback(struct timer_list *t) {
    printk(KERN_INFO "Kernel timer callback: count = %d\n", ++count);
    
    // 重新设置定时器(1秒后)
    mod_timer(&my_timer, jiffies + HZ);
}

static int __init timer_init(void) {
    printk(KERN_INFO "Initializing kernel timer module\n");
    
    // 初始化定时器
    timer_setup(&my_timer, timer_callback, 0);
    
    // 启动定时器(1秒后到期)
    mod_timer(&my_timer, jiffies + HZ);
    
    return 0;
}

static void __exit timer_exit(void) {
    // 删除定时器
    del_timer(&my_timer);
    printk(KERN_INFO "Kernel timer module unloaded, total callbacks: %d\n", count);
}

module_init(timer_init);
module_exit(timer_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple kernel timer example");

7. 最佳实践和注意事项

7.1 定时器使用准则

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <string.h>

// 1. 避免在信号处理函数中调用不可重入函数
void safe_timer_handler(int sig) {
    // 安全:只设置标志或使用原子操作
    static volatile sig_atomic_t flag = 0;
    flag = 1;
    
    // 不安全:printf, malloc等不可重入函数
    // printf("Timer!\n"); // 危险!
}

// 2. 处理定时器竞争条件
void race_condition_example() {
    printf("Timer race condition considerations:\n");
    printf("1. 设置定时器和检查标志需要同步\n");
    printf("2. 考虑使用原子操作或锁\n");
    printf("3. 在信号处理中做最少的工作\n");
}

// 3. 资源清理
void cleanup_timer(timer_t timer_id) {
    // 停止定时器
    struct itimerspec its;
    memset(&its, 0, sizeof(its));
    timer_settime(timer_id, 0, &its, NULL);
    
    // 删除定时器
    timer_delete(timer_id);
    printf("Timer cleaned up successfully.\n");
}

// 4. 错误处理
int robust_timer_create(timer_t *timer_id, void (*handler)(union sigval)) {
    struct sigevent sev;
    
    memset(&sev, 0, sizeof(sev));
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = handler;
    
    if (timer_create(CLOCK_REALTIME, &sev, timer_id) == -1) {
        perror("Failed to create timer");
        return -1;
    }
    
    return 0;
}

int main() {
    printf("Timer best practices:\n");
    printf("1. 选择适合的定时器类型\n");
    printf("2. 考虑精度和性能需求\n");
    printf("3. 正确处理信号和线程安全\n");
    printf("4. 总是清理定时器资源\n");
    printf("5. 处理可能的错误情况\n");
    
    return 0;
}

8. 性能比较和选择指南

定时器类型 精度 性能 适用场景
setitimer 微秒 中等 简单间隔定时
POSIX定时器 纳秒 复杂定时需求
高精度定时器 纳秒 很高 实时应用
内核定时器 毫秒 很高 内核开发

Linux定时器提供了灵活的定时功能,根据应用需求选择合适的定时器类型非常重要。对于用户空间应用,推荐使用POSIX定时器;对于高性能需求,考虑高精度定时器;对于内核开发,使用内核定时器API。


7.Linux ioctl 详解

1. ioctl 概述

ioctl(input/output control)是设备驱动程序中用来处理特殊设备控制命令的系统调用,用于实现设备特定的操作。

1.1 基本概念

  • 设备特定操作:无法用标准文件操作(read/write)实现的设备控制
  • 用户空间与内核空间通信:用户程序通过ioctl向驱动程序发送命令
  • 灵活性强:可以传递任意类型的数据结构

2. ioctl 基本用法

2.1 用户空间接口

#include <sys/ioctl.h>

int ioctl(int fd, unsigned long request, ...);

2.2 简单示例

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>

// 简单的ioctl示例
int main() {
    int fd;
    const char *device = "/dev/mydevice";
    
    // 打开设备文件
    fd = open(device, O_RDWR);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    
    int value = 100;
    
    // 发送ioctl命令
    if (ioctl(fd, 0x1234, &value) < 0) {
        perror("ioctl");
        close(fd);
        return -1;
    }
    
    printf("ioctl command sent successfully\n");
    close(fd);
    return 0;
}

3. ioctl 命令编码

3.1 命令编码规范

Linux内核提供了标准的ioctl命令编码宏:

#include <linux/ioctl.h>

// ioctl命令编码宏
_IO(type, nr)              // 无参数命令
_IOR(type, nr, datatype)   // 读命令(从驱动读取数据)
_IOW(type, nr, datatype)   // 写命令(向驱动写入数据)
_IOWR(type, nr, datatype)  // 读写命令

3.2 命令编码示例

// ioctl_commands.h - 用户空间和内核空间共享的头文件
#ifndef IOCTL_COMMANDS_H
#define IOCTL_COMMANDS_H

#include <linux/ioctl.h>

// 魔术字 - 每个设备唯一
#define MYDEVICE_IOC_MAGIC 'k'

// 命令序列号
#define MYDEVICE_IOC_RESET        _IO(MYDEVICE_IOC_MAGIC, 0)
#define MYDEVICE_IOC_GET_STATUS   _IOR(MYDEVICE_IOC_MAGIC, 1, int)
#define MYDEVICE_IOC_SET_VALUE    _IOW(MYDEVICE_IOC_MAGIC, 2, int)
#define MYDEVICE_IOC_GET_VALUE    _IOR(MYDEVICE_IOC_MAGIC, 3, int)
#define MYDEVICE_IOC_SET_CONFIG   _IOW(MYDEVICE_IOC_MAGIC, 4, struct myconfig)
#define MYDEVICE_IOC_GET_CONFIG   _IOR(MYDEVICE_IOC_MAGIC, 5, struct myconfig)

// 最大命令号
#define MYDEVICE_IOC_MAXNR 5

// 配置结构体
struct myconfig {
    int speed;
    int mode;
    char name[32];
};

#endif

4. 完整的驱动示例

4.1 字符设备驱动 with ioctl

// mydevice_driver.c - 内核模块
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>
#include "ioctl_commands.h"  // 共享的头文件

#define DEVICE_NAME "mydevice"
#define MAX_DEVICES 1

struct mydevice_data {
    struct cdev cdev;
    dev_t devno;
    int value;
    int status;
    struct myconfig config;
    struct mutex lock;  // 保护并发访问
};

static struct mydevice_data mydev;
static int device_open_count = 0;

static int mydevice_open(struct inode *inode, struct file *filp)
{
    mutex_lock(&mydev.lock);
    if (device_open_count) {
        mutex_unlock(&mydev.lock);
        return -EBUSY;
    }
    device_open_count++;
    mutex_unlock(&mydev.lock);
    
    filp->private_data = &mydev;
    return 0;
}

static int mydevice_release(struct inode *inode, struct file *filp)
{
    mutex_lock(&mydev.lock);
    device_open_count--;
    mutex_unlock(&mydev.lock);
    return 0;
}

static long mydevice_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct mydevice_data *dev = filp->private_data;
    int ret = 0;
    int tmp;
    struct myconfig config;
    
    // 检查命令类型
    if (_IOC_TYPE(cmd) != MYDEVICE_IOC_MAGIC) {
        pr_err("Invalid ioctl magic\n");
        return -ENOTTY;
    }
    
    if (_IOC_NR(cmd) > MYDEVICE_IOC_MAXNR) {
        pr_err("Invalid ioctl command number\n");
        return -ENOTTY;
    }
    
    mutex_lock(&dev->lock);
    
    switch (cmd) {
    case MYDEVICE_IOC_RESET:
        dev->value = 0;
        dev->status = 0;
        memset(&dev->config, 0, sizeof(dev->config));
        pr_info("Device reset\n");
        break;
        
    case MYDEVICE_IOC_GET_STATUS:
        tmp = dev->status;
        if (copy_to_user((int __user *)arg, &tmp, sizeof(tmp))) {
            ret = -EFAULT;
        }
        break;
        
    case MYDEVICE_IOC_SET_VALUE:
        if (copy_from_user(&tmp, (int __user *)arg, sizeof(tmp))) {
            ret = -EFAULT;
        } else {
            dev->value = tmp;
            pr_info("Value set to %d\n", dev->value);
        }
        break;
        
    case MYDEVICE_IOC_GET_VALUE:
        tmp = dev->value;
        if (copy_to_user((int __user *)arg, &tmp, sizeof(tmp))) {
            ret = -EFAULT;
        }
        break;
        
    case MYDEVICE_IOC_SET_CONFIG:
        if (copy_from_user(&config, (struct myconfig __user *)arg, sizeof(config))) {
            ret = -EFAULT;
        } else {
            dev->config = config;
            pr_info("Config set: speed=%d, mode=%d, name=%s\n",
                   dev->config.speed, dev->config.mode, dev->config.name);
        }
        break;
        
    case MYDEVICE_IOC_GET_CONFIG:
        if (copy_to_user((struct myconfig __user *)arg, &dev->config, sizeof(dev->config))) {
            ret = -EFAULT;
        }
        break;
        
    default:
        ret = -ENOTTY;
        break;
    }
    
    mutex_unlock(&dev->lock);
    return ret;
}

static const struct file_operations mydevice_fops = {
    .owner = THIS_MODULE,
    .open = mydevice_open,
    .release = mydevice_release,
    .unlocked_ioctl = mydevice_ioctl,
    .compat_ioctl = mydevice_ioctl,  // 兼容32位应用
};

static int __init mydevice_init(void)
{
    int ret;
    dev_t dev;
    
    // 分配设备号
    ret = alloc_chrdev_region(&dev, 0, MAX_DEVICES, DEVICE_NAME);
    if (ret < 0) {
        pr_err("Failed to allocate device number\n");
        return ret;
    }
    mydev.devno = dev;
    
    // 初始化互斥锁
    mutex_init(&mydev.lock);
    
    // 初始化设备数据
    mydev.value = 0;
    mydev.status = 0;
    memset(&mydev.config, 0, sizeof(mydev.config));
    strncpy(mydev.config.name, "default", sizeof(mydev.config.name) - 1);
    
    // 初始化cdev
    cdev_init(&mydev.cdev, &mydevice_fops);
    mydev.cdev.owner = THIS_MODULE;
    
    // 添加cdev到系统
    ret = cdev_add(&mydev.cdev, mydev.devno, MAX_DEVICES);
    if (ret) {
        pr_err("Failed to add cdev\n");
        unregister_chrdev_region(mydev.devno, MAX_DEVICES);
        return ret;
    }
    
    pr_info("MyDevice driver loaded, major=%d, minor=%d\n",
           MAJOR(mydev.devno), MINOR(mydev.devno));
    
    return 0;
}

static void __exit mydevice_exit(void)
{
    cdev_del(&mydev.cdev);
    unregister_chrdev_region(mydev.devno, MAX_DEVICES);
    mutex_destroy(&mydev.lock);
    pr_info("MyDevice driver unloaded\n");
}

module_init(mydevice_init);
module_exit(mydevice_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Example device driver with ioctl");

4.2 用户空间测试程序

// test_mydevice.c - 用户空间测试程序
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include "ioctl_commands.h"  // 共享的头文件

int main() {
    int fd;
    int ret;
    int value, status;
    struct myconfig config;
    
    printf("MyDevice ioctl test program\n");
    
    // 打开设备文件
    fd = open("/dev/mydevice", O_RDWR);
    if (fd < 0) {
        perror("open");
        printf("Make sure the device driver is loaded and device node exists\n");
        return -1;
    }
    
    printf("Device opened successfully\n");
    
    // 测试1: 重置设备
    printf("\n1. Resetting device...\n");
    if (ioctl(fd, MYDEVICE_IOC_RESET) < 0) {
        perror("ioctl RESET");
    } else {
        printf("Device reset successful\n");
    }
    
    // 测试2: 设置值
    printf("\n2. Setting value to 42...\n");
    value = 42;
    if (ioctl(fd, MYDEVICE_IOC_SET_VALUE, &value) < 0) {
        perror("ioctl SET_VALUE");
    } else {
        printf("Value set to %d\n", value);
    }
    
    // 测试3: 读取值
    printf("\n3. Reading value...\n");
    if (ioctl(fd, MYDEVICE_IOC_GET_VALUE, &value) < 0) {
        perror("ioctl GET_VALUE");
    } else {
        printf("Value read: %d\n", value);
    }
    
    // 测试4: 读取状态
    printf("\n4. Reading status...\n");
    if (ioctl(fd, MYDEVICE_IOC_GET_STATUS, &status) < 0) {
        perror("ioctl GET_STATUS");
    } else {
        printf("Status: %d\n", status);
    }
    
    // 测试5: 设置配置
    printf("\n5. Setting configuration...\n");
    memset(&config, 0, sizeof(config));
    config.speed = 9600;
    config.mode = 1;
    strncpy(config.name, "test_config", sizeof(config.name) - 1);
    
    if (ioctl(fd, MYDEVICE_IOC_SET_CONFIG, &config) < 0) {
        perror("ioctl SET_CONFIG");
    } else {
        printf("Configuration set:\n");
        printf("  Speed: %d\n", config.speed);
        printf("  Mode: %d\n", config.mode);
        printf("  Name: %s\n", config.name);
    }
    
    // 测试6: 读取配置
    printf("\n6. Reading configuration...\n");
    if (ioctl(fd, MYDEVICE_IOC_GET_CONFIG, &config) < 0) {
        perror("ioctl GET_CONFIG");
    } else {
        printf("Configuration read:\n");
        printf("  Speed: %d\n", config.speed);
        printf("  Mode: %d\n", config.mode);
        printf("  Name: %s\n", config.name);
    }
    
    close(fd);
    printf("\nTest completed\n");
    
    return 0;
}

5. 高级 ioctl 用法

5.1 可变长度数据传递

// 处理可变长度数据的ioctl示例

// 在共享头文件中添加
#define MYDEVICE_IOC_SEND_DATA    _IOW(MYDEVICE_IOC_MAGIC, 6, struct iovec)
#define MYDEVICE_IOC_RECV_DATA    _IOWR(MYDEVICE_IOC_MAGIC, 7, struct iovec)

// 在驱动中处理可变长度数据
case MYDEVICE_IOC_SEND_DATA: {
    struct iovec iov;
    void *buffer;
    
    if (copy_from_user(&iov, (struct iovec __user *)arg, sizeof(iov))) {
        ret = -EFAULT;
        break;
    }
    
    // 分配内核缓冲区
    buffer = kmalloc(iov.iov_len, GFP_KERNEL);
    if (!buffer) {
        ret = -ENOMEM;
        break;
    }
    
    // 拷贝用户数据到内核
    if (copy_from_user(buffer, iov.iov_base, iov.iov_len)) {
        ret = -EFAULT;
        kfree(buffer);
        break;
    }
    
    // 处理数据...
    pr_info("Received %zu bytes of data\n", iov.iov_len);
    
    kfree(buffer);
    break;
}

5.2 权限检查

#include <linux/cred.h>

static long mydevice_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    // 检查用户权限
    if (!capable(CAP_SYS_ADMIN)) {
        pr_warn("Permission denied for non-admin user\n");
        return -EPERM;
    }
    
    // 或者检查特定命令的权限
    switch (cmd) {
    case MYDEVICE_IOC_RESET:
        if (!capable(CAP_SYS_ADMIN)) {
            return -EPERM;
        }
        break;
    // ... 其他命令处理
    }
    
    return 0;
}

6. 内核与用户空间数据结构兼容性

6.1 处理32/64位兼容性

// 使用 compat_ioctl 处理32位应用
#ifdef CONFIG_COMPAT
static long mydevice_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    void __user *argp = compat_ptr(arg);
    
    switch (cmd) {
    case MYDEVICE_IOC_SET_CONFIG32:
        // 处理32位版本的数据结构
        break;
    // ... 其他兼容命令
    default:
        return mydevice_ioctl(filp, cmd, (unsigned long)argp);
    }
    
    return 0;
}
#endif

static const struct file_operations mydevice_fops = {
    .owner = THIS_MODULE,
    .open = mydevice_open,
    .release = mydevice_release,
    .unlocked_ioctl = mydevice_ioctl,
    .compat_ioctl = mydevice_compat_ioctl,
};

7. 调试和错误处理

7.1 ioctl 调试技巧

// 在驱动中添加调试信息
static long mydevice_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    // 记录ioctl调用
    pr_debug("ioctl called: cmd=0x%x, arg=0x%lx\n", cmd, arg);
    
    switch (cmd) {
    case MYDEVICE_IOC_SET_VALUE:
        pr_debug("SET_VALUE: arg=%ld\n", arg);
        break;
    // ... 其他命令
    }
    
    return ret;
}

// 在用户空间检查错误
void check_ioctl_error(int ret, const char *operation) {
    if (ret < 0) {
        switch (errno) {
        case ENOTTY:
            fprintf(stderr, "%s: Invalid ioctl command\n", operation);
            break;
        case EFAULT:
            fprintf(stderr, "%s: Bad address\n", operation);
            break;
        case EPERM:
            fprintf(stderr, "%s: Permission denied\n", operation);
            break;
        case EINVAL:
            fprintf(stderr, "%s: Invalid argument\n", operation);
            break;
        default:
            perror(operation);
            break;
        }
    }
}

8. 最佳实践

8.1 ioctl 设计准则

// 1. 使用唯一的魔术字
#define MYDEVICE_IOC_MAGIC 'k'  // 确保不与其他驱动冲突

// 2. 验证命令参数
if (_IOC_DIR(cmd) & _IOC_READ) {
    // 检查读权限
    ret = access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
} else if (_IOC_DIR(cmd) & _IOC_WRITE) {
    // 检查写权限
    ret = access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
}

// 3. 使用适当的锁机制
mutex_lock(&dev->lock);
// 处理命令
mutex_unlock(&dev->lock);

// 4. 处理并发访问
static atomic_t ioctl_in_use = ATOMIC_INIT(0);

if (atomic_cmpxchg(&ioctl_in_use, 0, 1) != 0) {
    return -EBUSY;
}
// 处理命令
atomic_set(&ioctl_in_use, 0);

// 5. 提供清晰的错误码
- ENOTTY:  无效的命令
- EFAULT:  错误的内存地址
- EINVAL:  无效的参数
- EPERM:   权限不足
- EBUSY:   设备忙

8.2 安全性考虑

// 1. 验证用户输入
case MYDEVICE_IOC_SET_CONFIG:
    if (copy_from_user(&config, (void __user *)arg, sizeof(config))) {
        return -EFAULT;
    }
    
    // 验证配置参数
    if (config.speed < 0 || config.speed > 1000000) {
        return -EINVAL;
    }
    
    // 确保字符串以null结尾
    config.name[sizeof(config.name) - 1] = '\0';
    break;

// 2. 限制缓冲区大小
#define MAX_DATA_SIZE 4096

case MYDEVICE_IOC_SEND_DATA:
    if (data_size > MAX_DATA_SIZE) {
        return -E2BIG;
    }

ioctl是Linux设备驱动开发中非常重要的机制,它提供了设备特定操作的灵活接口。正确使用ioctl需要仔细设计命令编码、处理并发访问、确保安全性,并提供清晰的错误处理。


8.Linux中断处理

1. Linux中断概述

中断是硬件与CPU通信的一种机制,当硬件需要CPU处理时,通过中断信号通知CPU。

1.1 中断类型

  • 硬件中断:由硬件设备产生(键盘、鼠标、网卡等)
  • 软件中断:由软件指令产生
  • 异常:CPU执行指令时产生的错误

1.2 中断处理特点

  • 异步执行:中断可以在任何时候发生
  • 中断上下文:执行环境限制较多
  • 上半部/下半部:将中断处理分为紧急和非紧急部分

2. 中断处理基础

2.1 注册中断处理程序

#include <linux/interrupt.h>

// 中断处理函数原型
irqreturn_t interrupt_handler(int irq, void *dev_id);

// 注册中断处理函数
int request_irq(unsigned int irq, irq_handler_t handler, 
                unsigned long flags, const char *name, void *dev);

// 释放中断
void free_irq(unsigned int irq, void *dev_id);

2.2 简单中断示例

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>

#define GPIO_IRQ 17  // GPIO引脚号
static int irq_number;

// 中断处理函数
static irqreturn_t gpio_interrupt_handler(int irq, void *dev_id)
{
    printk(KERN_INFO "Interrupt occurred on GPIO %d\n", irq);
    
    // 读取GPIO状态或其他处理
    // 注意:在中断上下文中不能睡眠!
    
    return IRQ_HANDLED;  // 中断已处理
}

static int __init my_interrupt_init(void)
{
    int ret;
    
    printk(KERN_INFO "Initializing interrupt module\n");
    
    // 注册中断处理程序
    ret = request_irq(GPIO_IRQ, gpio_interrupt_handler, 
                     IRQF_TRIGGER_RISING, "my_gpio_irq", NULL);
    
    if (ret) {
        printk(KERN_ERR "Failed to request IRQ %d\n", GPIO_IRQ);
        return ret;
    }
    
    irq_number = GPIO_IRQ;
    printk(KERN_INFO "Interrupt handler registered for IRQ %d\n", GPIO_IRQ);
    
    return 0;
}

static void __exit my_interrupt_exit(void)
{
    free_irq(irq_number, NULL);
    printk(KERN_INFO "Interrupt handler unregistered\n");
}

module_init(my_interrupt_init);
module_exit(my_interrupt_exit);
MODULE_LICENSE("GPL");

3. 中断标志和类型

3.1 中断标志

// 中断触发类型
IRQF_TRIGGER_RISING     // 上升沿触发
IRQF_TRIGGER_FALLING    // 下降沿触发  
IRQF_TRIGGER_HIGH       // 高电平触发
IRQF_TRIGGER_LOW        // 低电平触发

// 中断处理标志
IRQF_SHARED             // 共享中断
IRQF_ONESHOT            // 一次性中断,处理完成后需要重新使能
IRQF_TIMER              // 定时器中断
IRQF_NO_THREAD          // 禁止线程化处理
IRQF_EARLY_RESUME       // 早期恢复

3.2 共享中断示例

#include <linux/module.h>
#include <linux/interrupt.h>

#define SHARED_IRQ 9
static int irq = SHARED_IRQ;
static int dev_id = 1;

// 共享中断处理函数
static irqreturn_t shared_interrupt_handler(int irq, void *dev_id)
{
    int *my_id = (int *)dev_id;
    
    // 检查是否是我们的设备产生的中断
    if (!is_my_device_interrupt()) {
        return IRQ_NONE;  // 不是我们的中断
    }
    
    printk(KERN_INFO "Shared interrupt handled by device %d\n", *my_id);
    
    // 处理中断
    handle_device_interrupt();
    
    return IRQ_HANDLED;
}

static int __init shared_irq_init(void)
{
    int ret;
    
    // 注册共享中断
    ret = request_irq(irq, shared_interrupt_handler, 
                     IRQF_SHARED | IRQF_TRIGGER_RISING,
                     "my_shared_irq", &dev_id);
    
    if (ret) {
        printk(KERN_ERR "Failed to request shared IRQ\n");
        return ret;
    }
    
    printk(KERN_INFO "Shared interrupt handler registered\n");
    return 0;
}

static void __exit shared_irq_exit(void)
{
    free_irq(irq, &dev_id);
    printk(KERN_INFO "Shared interrupt handler unregistered\n");
}

4. 中断上下文限制

4.1 中断上下文的特点

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/sched.h>

static int test_irq;

static irqreturn_t test_interrupt_handler(int irq, void *dev_id)
{
    printk(KERN_INFO "Interrupt context information:\n");
    
    // 检查当前上下文
    if (in_interrupt()) {
        printk(KERN_INFO "  - We are in interrupt context\n");
    }
    
    if (in_irq()) {
        printk(KERN_INFO "  - We are in hard IRQ context\n");
    }
    
    if (in_softirq()) {
        printk(KERN_INFO "  - We are in softirq context\n");
    }
    
    // 中断上下文中不能做的事情:
    // 1. 不能访问用户空间
    // 2. 不能执行可能睡眠的操作
    // 3. 不能调用可能阻塞的函数
    
    // 错误的做法(会导致内核错误):
    // copy_to_user(...);      // 可能睡眠
    // kmalloc(GFP_KERNEL);    // 可能睡眠
    // mutex_lock(...);        // 可能睡眠
    
    // 正确的做法:
    // copy_to_user 的替代:先缓存数据,在进程上下文中处理
    // kmalloc(GFP_ATOMIC);    // 使用原子分配
    // spin_lock(...);         // 使用自旋锁
    
    return IRQ_HANDLED;
}

5. 底半部机制

由于中断处理程序的限制,Linux将中断处理分为两部分:

  • 顶半部:紧急处理,在中断上下文中执行
  • 底半部:非紧急处理,可以睡眠和阻塞

5.1 软中断 (Softirq)

// 软中断是内核底层的底半部机制
// 通常由内核核心代码使用,驱动程序较少直接使用

5.2 Tasklet

#include <linux/interrupt.h>

// Tasklet声明和初始化
DECLARE_TASKLET(my_tasklet, tasklet_function, (unsigned long)data);
DECLARE_TASKLET_DISABLED(my_tasklet, tasklet_function, (unsigned long)data);

// Tasklet处理函数
void tasklet_function(unsigned long data)
{
    // 这里可以执行较耗时的操作
    // 但仍然在软中断上下文中,不能睡眠
    printk(KERN_INFO "Tasklet executing with data: %lu\n", data);
}

// 在中断处理函数中调度tasklet
static irqreturn_t irq_handler_with_tasklet(int irq, void *dev_id)
{
    // 紧急处理
    handle_urgent_work();
    
    // 调度tasklet处理非紧急工作
    tasklet_schedule(&my_tasklet);
    
    return IRQ_HANDLED;
}

// 初始化和清理
static int __init tasklet_example_init(void)
{
    // 初始化tasklet
    tasklet_init(&my_tasklet, tasklet_function, 0x1234);
    return 0;
}

static void __exit tasklet_example_exit(void)
{
    // 禁用并等待tasklet完成
    tasklet_kill(&my_tasklet);
}

5.3 工作队列 (Workqueue)

#include <linux/workqueue.h>

// 工作队列声明
static struct workqueue_struct *my_wq;
static struct work_struct my_work;

// 工作处理函数
void work_handler(struct work_struct *work)
{
    // 在进程上下文中执行,可以睡眠
    printk(KERN_INFO "Work queue handler executing\n");
    
    // 可以执行耗时的操作
    // 可以调用可能睡眠的函数
    msleep(1000);  // 在工作队列中可以睡眠
    
    // 可以分配内存(可能睡眠)
    char *buf = kmalloc(1024, GFP_KERNEL);
    if (buf) {
        // 处理数据
        kfree(buf);
    }
}

// 在中断处理函数中调度工作
static irqreturn_t irq_handler_with_workqueue(int irq, void *dev_id)
{
    // 紧急处理
    handle_urgent_work();
    
    // 调度工作队列处理非紧急工作
    queue_work(my_wq, &my_work);
    
    return IRQ_HANDLED;
}

// 延迟工作队列
static struct delayed_work my_delayed_work;

void delayed_work_handler(struct work_struct *work)
{
    printk(KERN_INFO "Delayed work executed after 2 seconds\n");
}

static int __init workqueue_example_init(void)
{
    // 创建工作队列
    my_wq = create_singlethread_workqueue("my_workqueue");
    if (!my_wq) {
        return -ENOMEM;
    }
    
    // 初始化工作
    INIT_WORK(&my_work, work_handler);
    INIT_DELAYED_WORK(&my_delayed_work, delayed_work_handler);
    
    // 调度延迟工作(2秒后执行)
    queue_delayed_work(my_wq, &my_delayed_work, msecs_to_jiffies(2000));
    
    return 0;
}

static void __exit workqueue_example_exit(void)
{
    // 取消延迟工作
    cancel_delayed_work(&my_delayed_work);
    
    // 刷新并销毁工作队列
    flush_workqueue(my_wq);
    destroy_workqueue(my_wq);
}

6. 完整的中断处理示例

6.1 网络设备中断处理

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>

struct my_net_device {
    struct net_device *netdev;
    int irq;
    struct napi_struct napi;
    spinlock_t lock;
    struct work_struct tx_work;
};

// NAPI轮询函数
int my_napi_poll(struct napi_struct *napi, int budget)
{
    struct my_net_device *dev = container_of(napi, struct my_net_device, napi);
    int work_done = 0;
    
    // 处理接收的数据包
    while (work_done < budget && has_rx_packets(dev)) {
        if (process_rx_packet(dev) < 0) {
            break;
        }
        work_done++;
    }
    
    // 如果所有数据包都处理完了,禁用NAPI
    if (work_done < budget) {
        napi_complete_done(napi, work_done);
        // 重新使能中断
        enable_irq(dev->irq);
    }
    
    return work_done;
}

// 中断处理函数
static irqreturn_t netdev_interrupt_handler(int irq, void *dev_id)
{
    struct my_net_device *dev = dev_id;
    
    // 禁用中断,使用NAPI处理
    disable_irq_nosync(irq);
    
    // 调度NAPI
    if (napi_schedule_prep(&dev->napi)) {
        __napi_schedule(&dev->napi);
    }
    
    return IRQ_HANDLED;
}

// 发送工作队列处理函数
void tx_work_handler(struct work_struct *work)
{
    struct my_net_device *dev = container_of(work, struct my_net_device, tx_work);
    
    // 处理发送队列
    spin_lock_bh(&dev->lock);
    process_tx_queue(dev);
    spin_unlock_bh(&dev->lock);
}

// 网络设备打开函数
int netdev_open(struct net_device *ndev)
{
    struct my_net_device *dev = netdev_priv(ndev);
    int ret;
    
    // 初始化NAPI
    netif_napi_add(ndev, &dev->napi, my_napi_poll, 64);
    
    // 注册中断处理程序
    ret = request_irq(dev->irq, netdev_interrupt_handler,
                     IRQF_SHARED, ndev->name, dev);
    if (ret) {
        netif_napi_del(&dev->napi);
        return ret;
    }
    
    // 初始化工作队列
    INIT_WORK(&dev->tx_work, tx_work_handler);
    
    // 启动设备
    netif_start_queue(ndev);
    
    return 0;
}

6.2 中断处理的状态管理

#include <linux/interrupt.h>
#include <linux/bitops.h>

struct my_device {
    int irq;
    unsigned long status;
    spinlock_t lock;
    wait_queue_head_t wait_queue;
};

#define STATUS_IRQ_ENABLED   0
#define STATUS_IRQ_PENDING   1
#define STATUS_DEVICE_READY  2

static irqreturn_t stateful_interrupt_handler(int irq, void *dev_id)
{
    struct my_device *dev = dev_id;
    unsigned long flags;
    
    spin_lock_irqsave(&dev->lock, flags);
    
    // 标记中断待处理
    set_bit(STATUS_IRQ_PENDING, &dev->status);
    
    // 处理硬件状态
    read_device_status(dev);
    
    // 唤醒等待进程
    wake_up_interruptible(&dev->wait_queue);
    
    spin_unlock_irqrestore(&dev->lock, flags);
    
    return IRQ_HANDLED;
}

// 使能/禁用中断
void enable_my_irq(struct my_device *dev)
{
    unsigned long flags;
    
    spin_lock_irqsave(&dev->lock, flags);
    if (!test_and_set_bit(STATUS_IRQ_ENABLED, &dev->status)) {
        enable_irq(dev->irq);
    }
    spin_unlock_irqrestore(&dev->lock, flags);
}

void disable_my_irq(struct my_device *dev)
{
    unsigned long flags;
    
    spin_lock_irqsave(&dev->lock, flags);
    if (test_and_clear_bit(STATUS_IRQ_ENABLED, &dev->status)) {
        disable_irq(dev->irq);
    }
    spin_unlock_irqrestore(&dev->lock, flags);
}

7. 中断统计和调试

7.1 中断统计信息

#include <linux/interrupt.h>
#include <linux/proc_fs.h>

// 查看系统中断统计
static int __init check_interrupts(void)
{
    int i;
    
    printk(KERN_INFO "Interrupt statistics:\n");
    
    for (i = 0; i < NR_IRQS; i++) {
        struct irq_desc *desc = irq_to_desc(i);
        if (desc && desc->action) {
            printk(KERN_INFO "IRQ %d: %s, count: %lu\n",
                   i, desc->action->name, kstat_irqs(i));
        }
    }
    
    return 0;
}

// 在/proc/interrupts中的自定义信息
static int proc_interrupts_show(struct seq_file *m, void *v)
{
    int i;
    
    seq_printf(m, "%-16s %10s %10s %-20s\n", 
               "IRQ", "COUNT", "DEVICE", "TYPE");
    
    for (i = 0; i < NR_IRQS; i++) {
        struct irq_desc *desc = irq_to_desc(i);
        if (desc && desc->action) {
            seq_printf(m, "%-16d %10lu %10s %-20s\n",
                       i, kstat_irqs(i),
                       desc->action->name, "edge");
        }
    }
    
    return 0;
}

7.2 调试技巧

// 中断调试宏
#define DEBUG_INTERRUPTS 1

#ifdef DEBUG_INTERRUPTS
#define IRQ_DEBUG(fmt, args...) \
    printk(KERN_DEBUG "IRQ_DEBUG: " fmt, ##args)
#else
#define IRQ_DEBUG(fmt, args...) 
#endif

static irqreturn_t debug_interrupt_handler(int irq, void *dev_id)
{
    IRQ_DEBUG("Entering interrupt handler for IRQ %d\n", irq);
    
    // 记录进入时间
    unsigned long start_jiffies = jiffies;
    
    // 中断处理...
    
    // 记录处理时间
    unsigned long duration = jiffies - start_jiffies;
    if (duration > HZ/100) {  // 超过10ms
        printk(KERN_WARNING "Interrupt handler for IRQ %d took %lu jiffies\n",
               irq, duration);
    }
    
    IRQ_DEBUG("Exiting interrupt handler for IRQ %d\n", irq);
    
    return IRQ_HANDLED;
}

8. 最佳实践

8.1 中断处理准则

// 1. 保持中断处理程序简短
static irqreturn_t good_handler(int irq, void *dev_id)
{
    // 只做必要的最小工作
    acknowledge_interrupt();
    read_critical_data();
    
    // 调度底半部处理其他工作
    schedule_work(&my_work);
    
    return IRQ_HANDLED;
}

// 2. 使用适当的锁
static irqreturn_t handler_with_lock(int irq, void *dev_id)
{
    struct my_device *dev = dev_id;
    unsigned long flags;
    
    // 使用自旋锁保护共享数据
    spin_lock_irqsave(&dev->lock, flags);
    
    // 访问共享数据
    dev->interrupt_count++;
    
    spin_unlock_irqrestore(&dev->lock, flags);
    
    return IRQ_HANDLED;
}

// 3. 正确处理错误
static int __init robust_irq_init(void)
{
    int ret;
    
    ret = request_irq(irq, handler, flags, name, dev);
    if (ret == -EBUSY) {
        printk(KERN_ERR "IRQ %d is busy\n", irq);
        // 尝试其他IRQ或共享
    } else if (ret == -EINVAL) {
        printk(KERN_ERR "Invalid IRQ parameters\n");
    } else if (ret) {
        printk(KERN_ERR "Failed to request IRQ: %d\n", ret);
    }
    
    return ret;
}

// 4. 资源清理
static void __exit cleanup_irq(void)
{
    // 确保所有底半部处理完成
    flush_workqueue(my_wq);
    tasklet_kill(&my_tasklet);
    
    // 释放中断
    free_irq(irq_number, dev_id);
    
    // 销毁工作队列
    destroy_workqueue(my_wq);
}

8.2 性能优化

// 1. 使用NAPI处理网络中断
// 2. 合理选择底半部机制
//   - 短时间任务:tasklet
//   - 可能睡眠的任务:工作队列
//   - 网络设备:NAPI

// 3. 中断亲和性(设置CPU亲和性)
static void set_irq_affinity(int irq, int cpu)
{
    cpumask_t mask;
    
    cpumask_clear(&mask);
    cpumask_set_cpu(cpu, &mask);
    
    if (irq_set_affinity(irq, &mask)) {
        printk(KERN_WARNING "Failed to set IRQ affinity\n");
    }
}

// 4. 避免中断风暴
static irqreturn_t rate_limited_handler(int irq, void *dev_id)
{
    static unsigned long last_time = 0;
    unsigned long now = jiffies;
    
    // 限制中断频率(最少间隔10ms)
    if (time_before(now, last_time + HZ/100)) {
        return IRQ_HANDLED;
    }
    last_time = now;
    
    // 正常中断处理...
    return IRQ_HANDLED;
}

中断处理是Linux设备驱动开发中的核心内容。正确的中断处理需要考虑性能、并发、资源管理等多个方面。通过合理使用顶半部和底半部机制,可以创建高效可靠的中断处理程序。


9.Linux平台总线模型

1. 平台总线模型概述

平台总线模型是Linux内核中用于描述片上系统(SoC)设备的一种抽象机制,用于管理那些没有物理总线的"伪"设备。

1.1 基本概念

  • 平台设备(Platform Device):集成在SoC中的设备,如GPIO控制器、中断控制器等
  • 平台驱动(Platform Driver):与平台设备匹配的驱动程序
  • 平台总线(Platform Bus):虚拟总线,负责设备和驱动的匹配

1.2 平台总线模型优势

  • 统一设备管理接口
  • 支持设备树(Device Tree)描述
  • 简化片上系统设备驱动开发

2. 平台设备注册

2.1 静态注册平台设备

#include <linux/platform_device.h>

// 平台设备资源定义
static struct resource my_device_resources[] = {
    [0] = {
        .start = 0x10000000,    // 设备寄存器基地址
        .end   = 0x100000FF,    // 设备寄存器结束地址
        .flags = IORESOURCE_MEM, // 内存资源
    },
    [1] = {
        .start = 42,            // 中断号
        .end   = 42,
        .flags = IORESOURCE_IRQ, // 中断资源
    },
};

// 平台设备定义
static struct platform_device my_platform_device = {
    .name = "my_device",        // 设备名称,用于匹配驱动
    .id = -1,                   // 设备实例ID
    .num_resources = ARRAY_SIZE(my_device_resources),
    .resource = my_device_resources,
    .dev = {
        .platform_data = NULL,  // 设备私有数据
    },
};

// 模块初始化时注册设备
static int __init my_device_init(void)
{
    int ret;
    
    ret = platform_device_register(&my_platform_device);
    if (ret) {
        pr_err("Failed to register platform device\n");
        return ret;
    }
    
    pr_info("Platform device registered successfully\n");
    return 0;
}

// 模块退出时注销设备
static void __exit my_device_exit(void)
{
    platform_device_unregister(&my_platform_device);
    pr_info("Platform device unregistered\n");
}

2.2 动态注册平台设备

#include <linux/platform_device.h>

static struct platform_device *my_device;

static int __init dynamic_device_init(void)
{
    int ret;
    
    // 动态分配平台设备
    my_device = platform_device_alloc("my_dynamic_device", -1);
    if (!my_device) {
        pr_err("Failed to allocate platform device\n");
        return -ENOMEM;
    }
    
    // 设置设备资源(可选)
    struct resource res[] = {
        {
            .start = 0x20000000,
            .end = 0x200000FF,
            .flags = IORESOURCE_MEM,
        },
    };
    
    ret = platform_device_add_resources(my_device, res, ARRAY_SIZE(res));
    if (ret) {
        pr_err("Failed to add resources\n");
        goto err_put_device;
    }
    
    // 添加设备到系统
    ret = platform_device_add(my_device);
    if (ret) {
        pr_err("Failed to add platform device\n");
        goto err_put_device;
    }
    
    pr_info("Dynamic platform device registered\n");
    return 0;

err_put_device:
    platform_device_put(my_device);
    return ret;
}

static void __exit dynamic_device_exit(void)
{
    platform_device_unregister(my_device);
    pr_info("Dynamic platform device unregistered\n");
}

3. 平台驱动注册

3.1 基本平台驱动

#include <linux/platform_device.h>
#include <linux/io.h>

struct my_driver_data {
    void __iomem *reg_base;
    int irq_number;
    struct device *dev;
};

// 探测函数 - 当设备与驱动匹配时调用
static int my_driver_probe(struct platform_device *pdev)
{
    struct my_driver_data *data;
    struct resource *res;
    int ret;
    
    pr_info("Platform driver probe called for device: %s\n", pdev->name);
    
    // 分配驱动私有数据
    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data) {
        return -ENOMEM;
    }
    
    // 获取内存资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "No memory resource\n");
        return -ENODEV;
    }
    
    // 映射设备寄存器
    data->reg_base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(data->reg_base)) {
        dev_err(&pdev->dev, "Failed to map registers\n");
        return PTR_ERR(data->reg_base);
    }
    
    // 获取中断资源
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (res) {
        data->irq_number = res->start;
        dev_info(&pdev->dev, "IRQ number: %d\n", data->irq_number);
    }
    
    // 获取平台数据
    if (pdev->dev.platform_data) {
        dev_info(&pdev->dev, "Got platform data\n");
        // 处理平台数据...
    }
    
    data->dev = &pdev->dev;
    
    // 保存私有数据
    platform_set_drvdata(pdev, data);
    
    // 设备初始化...
    dev_info(&pdev->dev, "Device probed successfully\n");
    
    return 0;
}

// 移除函数 - 当设备移除或驱动卸载时调用
static int my_driver_remove(struct platform_device *pdev)
{
    struct my_driver_data *data = platform_get_drvdata(pdev);
    
    dev_info(&pdev->dev, "Platform driver remove called\n");
    
    // 清理资源
    // 设备寄存器会自动取消映射(因为使用了devm_ioremap_resource)
    
    return 0;
}

// 平台驱动定义
static struct platform_driver my_platform_driver = {
    .probe = my_driver_probe,
    .remove = my_driver_remove,
    .driver = {
        .name = "my_device",           // 与平台设备名称匹配
        .owner = THIS_MODULE,
        .of_match_table = NULL,        // 设备树匹配表(后面介绍)
    },
};

// 模块初始化
static int __init my_driver_init(void)
{
    int ret;
    
    ret = platform_driver_register(&my_platform_driver);
    if (ret) {
        pr_err("Failed to register platform driver\n");
        return ret;
    }
    
    pr_info("Platform driver registered successfully\n");
    return 0;
}

// 模块退出
static void __exit my_driver_exit(void)
{
    platform_driver_unregister(&my_platform_driver);
    pr_info("Platform driver unregistered\n");
}

module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");

4. 设备树支持

4.1 设备树绑定

// 在驱动中添加设备树支持
#include <linux/of.h>
#include <linux/of_device.h>

// 设备树兼容性匹配表
static const struct of_device_id my_driver_of_match[] = {
    { .compatible = "vendor,my-device", .data = NULL },
    { .compatible = "vendor,my-device-v2", .data = (void *)1 },
    {}, // 空条目,表示结束
};
MODULE_DEVICE_TABLE(of, my_driver_of_match);

// 从设备树获取资源
static int my_driver_probe_dt(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    const struct of_device_id *match;
    int ret;
    
    if (!np) {
        dev_err(&pdev->dev, "No device tree node\n");
        return -ENODEV;
    }
    
    // 检查设备兼容性
    match = of_match_device(my_driver_of_match, &pdev->dev);
    if (!match) {
        dev_err(&pdev->dev, "No compatible device found\n");
        return -ENODEV;
    }
    
    dev_info(&pdev->dev, "Compatible with: %s\n", match->compatible);
    
    // 从设备树获取寄存器地址
    ret = of_address_to_resource(np, 0, &pdev->resource[0]);
    if (ret) {
        dev_err(&pdev->dev, "Failed to get memory resource from DT\n");
        return ret;
    }
    
    // 从设备树获取中断
    pdev->num_resources = 1; // 内存资源
    ret = of_irq_to_resource_table(np, &pdev->resource[1], 1);
    if (ret > 0) {
        pdev->num_resources += ret;
    }
    
    return 0;
}

// 更新平台驱动定义
static struct platform_driver my_platform_driver = {
    .probe = my_driver_probe,
    .remove = my_driver_remove,
    .driver = {
        .name = "my_device",
        .owner = THIS_MODULE,
        .of_match_table = my_driver_of_match,  // 添加设备树匹配表
    },
};

4.2 设备树节点示例

// 设备树源文件 (.dts)
/ {
    compatible = "vendor,board";
    
    my_device: my_device@10000000 {
        compatible = "vendor,my-device", "vendor,my-device-v2";
        reg = <0x10000000 0x100>;      // 寄存器地址和大小
        interrupts = <0 42 4>;         // 中断号
        clock-frequency = <50000000>;  // 自定义属性
        vendor,custom-property = "hello"; // 厂商特定属性
        
        // 子节点
        child-device {
            compatible = "vendor,child-device";
            reg = <0x10000100 0x20>;
        };
    };
};

5. 完整的平台设备驱动示例

5.1 简单的GPIO控制器驱动

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

#define DRIVER_NAME "my_gpio_controller"

struct my_gpio_data {
    void __iomem *reg_base;
    struct gpio_chip gpio_chip;
    int ngpios;
    spinlock_t lock;
};

static int my_gpio_get(struct gpio_chip *chip, unsigned offset)
{
    struct my_gpio_data *data = gpiochip_get_data(chip);
    u32 value;
    
    value = readl(data->reg_base);
    return !!(value & (1 << offset));
}

static void my_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
    struct my_gpio_data *data = gpiochip_get_data(chip);
    unsigned long flags;
    u32 reg;
    
    spin_lock_irqsave(&data->lock, flags);
    
    reg = readl(data->reg_base);
    if (value)
        reg |= (1 << offset);
    else
        reg &= ~(1 << offset);
    writel(reg, data->reg_base);
    
    spin_unlock_irqrestore(&data->lock, flags);
}

static int my_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
    // 设置GPIO为输入方向
    return 0;
}

static int my_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
{
    // 设置GPIO为输出方向并设置值
    my_gpio_set(chip, offset, value);
    return 0;
}

static int my_gpio_probe(struct platform_device *pdev)
{
    struct my_gpio_data *data;
    struct device_node *np = pdev->dev.of_node;
    struct resource *res;
    int ret;
    
    dev_info(&pdev->dev, "Probing GPIO controller\n");
    
    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;
    
    // 获取寄存器资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "No memory resource\n");
        return -ENODEV;
    }
    
    data->reg_base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(data->reg_base)) {
        dev_err(&pdev->dev, "Failed to map registers\n");
        return PTR_ERR(data->reg_base);
    }
    
    // 从设备树获取GPIO数量
    if (of_property_read_u32(np, "ngpios", &data->ngpios)) {
        data->ngpios = 32; // 默认值
    }
    
    // 初始化自旋锁
    spin_lock_init(&data->lock);
    
    // 设置GPIO芯片
    data->gpio_chip.label = DRIVER_NAME;
    data->gpio_chip.parent = &pdev->dev;
    data->gpio_chip.owner = THIS_MODULE;
    data->gpio_chip.get = my_gpio_get;
    data->gpio_chip.set = my_gpio_set;
    data->gpio_chip.direction_input = my_gpio_direction_input;
    data->gpio_chip.direction_output = my_gpio_direction_output;
    data->gpio_chip.base = -1; // 动态分配基号
    data->gpio_chip.ngpio = data->ngpios;
    data->gpio_chip.of_node = np;
    
    // 注册GPIO控制器
    ret = devm_gpiochip_add_data(&pdev->dev, &data->gpio_chip, data);
    if (ret) {
        dev_err(&pdev->dev, "Failed to register GPIO chip\n");
        return ret;
    }
    
    platform_set_drvdata(pdev, data);
    
    dev_info(&pdev->dev, "GPIO controller registered with %d GPIOs\n", data->ngpios);
    return 0;
}

static int my_gpio_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "GPIO controller removed\n");
    return 0;
}

static const struct of_device_id my_gpio_of_match[] = {
    { .compatible = "vendor,my-gpio", },
    {},
};
MODULE_DEVICE_TABLE(of, my_gpio_of_match);

static struct platform_driver my_gpio_driver = {
    .probe = my_gpio_probe,
    .remove = my_gpio_remove,
    .driver = {
        .name = DRIVER_NAME,
        .of_match_table = my_gpio_of_match,
        .owner = THIS_MODULE,
    },
};

module_platform_driver(my_gpio_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple GPIO controller platform driver");

6. 电源管理支持

6.1 挂起和恢复

#include <linux/pm.h>

static int my_driver_suspend(struct device *dev)
{
    struct my_driver_data *data = dev_get_drvdata(dev);
    
    dev_info(dev, "Suspending device\n");
    
    // 保存设备状态
    // 禁用中断
    // 进入低功耗模式
    
    return 0;
}

static int my_driver_resume(struct device *dev)
{
    struct my_driver_data *data = dev_get_drvdata(dev);
    
    dev_info(dev, "Resuming device\n");
    
    // 恢复设备状态
    // 重新使能中断
    // 退出低功耗模式
    
    return 0;
}

static const struct dev_pm_ops my_driver_pm_ops = {
    .suspend = my_driver_suspend,
    .resume = my_driver_resume,
    .freeze = my_driver_suspend,
    .thaw = my_driver_resume,
    .poweroff = my_driver_suspend,
    .restore = my_driver_resume,
};

// 在平台驱动中包含电源管理操作
static struct platform_driver my_platform_driver = {
    .probe = my_driver_probe,
    .remove = my_driver_remove,
    .driver = {
        .name = "my_device",
        .owner = THIS_MODULE,
        .of_match_table = my_driver_of_match,
        .pm = &my_driver_pm_ops,  // 添加电源管理支持
    },
};

7. 多设备实例支持

7.1 支持多个设备实例

// 在驱动中支持多个设备实例
static int my_driver_probe(struct platform_device *pdev)
{
    struct my_driver_data *data;
    int instance_id;
    
    // 获取设备实例ID
    instance_id = pdev->id;
    if (instance_id < 0) {
        // 从设备树获取实例信息
        instance_id = of_alias_get_id(pdev->dev.of_node, "my_device");
        if (instance_id < 0)
            instance_id = 0; // 默认实例
    }
    
    dev_info(&pdev->dev, "Probing device instance %d\n", instance_id);
    
    // 为每个实例分配独立的数据结构
    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;
    
    data->instance_id = instance_id;
    
    // 设备特定的初始化...
    
    platform_set_drvdata(pdev, data);
    return 0;
}

8. 调试和最佳实践

8.1 调试技巧

// 在驱动中添加调试支持
#define DEBUG

#ifdef DEBUG
#define DRV_DEBUG(fmt, args...) \
    dev_dbg(&pdev->dev, "DEBUG: " fmt, ##args)
#else
#define DRV_DEBUG(fmt, args...)
#endif

static int my_driver_probe(struct platform_device *pdev)
{
    DRV_DEBUG("Starting probe\n");
    
    // 打印设备资源信息
    struct resource *res;
    int i;
    
    for (i = 0; i < pdev->num_resources; i++) {
        res = &pdev->resource[i];
        if (resource_type(res) == IORESOURCE_MEM) {
            DRV_DEBUG("Memory resource: %pr\n", res);
        } else if (resource_type(res) == IORESOURCE_IRQ) {
            DRV_DEBUG("IRQ resource: %pr\n", res);
        }
    }
    
    return 0;
}

8.2 最佳实践

// 1. 使用设备资源管理API
static int good_probe(struct platform_device *pdev)
{
    // 使用devm_系列函数自动管理资源
    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    data->reg_base = devm_ioremap_resource(&pdev->dev, res);
    data->irq = platform_get_irq(pdev, 0);
    
    // 错误时资源会自动释放
    return 0;
}

// 2. 正确的错误处理
static int robust_probe(struct platform_device *pdev)
{
    int ret;
    
    ret = some_initialization();
    if (ret) {
        dev_err(&pdev->dev, "Initialization failed: %d\n", ret);
        return ret;
    }
    
    // 使用goto进行清晰的错误处理
    ret = setup_resource_a();
    if (ret)
        goto err_cleanup_a;
        
    ret = setup_resource_b();
    if (ret)
        goto err_cleanup_b;
        
    return 0;
    
err_cleanup_b:
    cleanup_resource_b();
err_cleanup_a:
    cleanup_resource_a();
    return ret;
}

// 3. 支持模块参数
static unsigned int debug_level;
module_param(debug_level, uint, 0644);
MODULE_PARM_DESC(debug_level, "Debug level (0=off, 1=basic, 2=verbose)");

平台总线模型是现代Linux设备驱动开发的核心机制,特别是在嵌入式系统和SoC平台上。通过合理使用平台设备和驱动,结合设备树描述,可以创建可移植、易维护的设备驱动程序。


10.Linux设备树详解及实例

1. 设备树概述

设备树(Device Tree)是一种描述硬件配置的数据结构,用于将硬件信息从内核代码中分离出来。

1.1 设备树基本概念

  • 源文件.dts - 设备树源文件,.dtsi - 包含文件
  • 编译文件.dtb - 设备树二进制文件
  • 节点:描述设备或总线的基本单位
  • 属性:描述设备的特性、地址、中断等

1.2 设备树优势

  • 硬件信息与内核代码分离
  • 支持同一内核镜像在不同硬件平台运行
  • 简化BSP(板级支持包)开发

2. 设备树语法基础

2.1 基本结构

// 示例:简单的设备树
/dts-v1/;

/ {
    // 根节点
    compatible = "vendor,board-name", "vendor,board-family";
    model = "Vendor Board Version 1.0";
    #address-cells = <1>;  // 子节点地址用1个32位数字表示
    #size-cells = <1>;     // 子节点大小用1个32位数字表示
    
    // CPU节点
    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        
        cpu@0 {
            compatible = "arm,cortex-a53";
            device_type = "cpu";
            reg = <0x0>;
            clock-frequency = <1200000000>;
        };
    };
    
    // 内存节点
    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>; // 起始地址0x80000000,大小1GB
    };
    
    // 保留内存
    reserved-memory {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;
        
        linux,cma {
            compatible = "shared-dma-pool";
            reusable;
            size = <0x10000000>; // 256MB
            alignment = <0x2000>;
            linux,cma-default;
        };
    };
};

2.2 常用属性详解

/ {
    // 兼容性属性 - 最重要的属性
    compatible = "vendor,board", "vendor,soc", "arm,board";
    
    // 模型描述
    model = "Vendor Development Board";
    
    // 地址和大小单元定义
    #address-cells = <1>;  // 地址用1个cell表示
    #size-cells = <1>;     // 大小用1个cell表示
    
    // 中断控制器属性
    interrupt-parent = <&intc>;  // 指向中断控制器
    
    // 选择总线
    chosen {
        bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait";
        stdout-path = &uart0;
    };
    
    // 别名
    aliases {
        serial0 = &uart0;
        ethernet0 = &eth0;
        mmc0 = &sdhci0;
    };
};

3. 常用设备节点实例

3.1 UART串口设备

/ {
    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "simple-bus";
        ranges;
        
        uart0: serial@10000000 {
            compatible = "vendor,uart", "ns16550a";
            reg = <0x10000000 0x1000>;
            interrupts = <0 10 4>;        // SPI 10, 电平触发
            clocks = <&clk_uart>;
            clock-frequency = <115200>;
            status = "okay";
            
            // FIFO设置
            fifo-size = <16>;
            auto-flow-control;
        };
        
        uart1: serial@10001000 {
            compatible = "vendor,uart";
            reg = <0x10001000 0x1000>;
            interrupts = <0 11 4>;
            clocks = <&clk_uart>;
            status = "disabled";          // 默认禁用
        };
    };
};

3.2 I2C控制器及设备

/ {
    soc {
        i2c0: i2c@20000000 {
            compatible = "vendor,i2c";
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <0x20000000 0x1000>;
            interrupts = <0 20 4>;
            clocks = <&clk_i2c>;
            clock-frequency = <100000>;   // 标准模式100kHz
            status = "okay";
            
            // I2C设备:EEPROM
            eeprom@50 {
                compatible = "atmel,24c02";
                reg = <0x50>;
                pagesize = <16>;
            };
            
            // I2C设备:温度传感器
            temp_sensor: lm75@48 {
                compatible = "national,lm75";
                reg = <0x48>;
            };
            
            // I2C设备:RTC
            rtc: ds1339@68 {
                compatible = "dallas,ds1339";
                reg = <0x68>;
            };
        };
        
        i2c1: i2c@20001000 {
            compatible = "vendor,i2c";
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <0x20001000 0x1000>;
            interrupts = <0 21 4>;
            clocks = <&clk_i2c>;
            clock-frequency = <400000>;   // 快速模式400kHz
            status = "okay";
            
            // I2C设备:音频编解码器
            codec: wm8960@1a {
                compatible = "wlf,wm8960";
                reg = <0x1a>;
                #sound-dai-cells = <0>;
            };
        };
    };
};

3.3 SPI控制器及设备

/ {
    soc {
        spi0: spi@30000000 {
            compatible = "vendor,spi";
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <0x30000000 0x1000>;
            interrupts = <0 30 4>;
            clocks = <&clk_spi>;
            num-cs = <2>;                // 2个片选信号
            status = "okay";
            
            // SPI Flash设备
            flash@0 {
                compatible = "jedec,spi-nor";
                reg = <0>;               // CS0
                spi-max-frequency = <50000000>;
                #address-cells = <1>;
                #size-cells = <1>;
                
                partition@0 {
                    label = "bootloader";
                    reg = <0x00000000 0x00100000>;
                };
                partition@100000 {
                    label = "kernel";
                    reg = <0x00100000 0x00500000>;
                };
                partition@600000 {
                    label = "rootfs";
                    reg = <0x00600000 0x01a00000>;
                };
            };
            
            // SPI ADC设备
            adc@1 {
                compatible = "ti,adc128s052";
                reg = <1>;               // CS1
                spi-max-frequency = <1000000>;
                vref-supply = <&vref_reg>;
            };
        };
    };
};

3.4 GPIO控制器

/ {
    soc {
        gpio0: gpio@40000000 {
            compatible = "vendor,gpio";
            reg = <0x40000000 0x1000>;
            interrupts = <0 40 4>;
            #gpio-cells = <2>;
            gpio-controller;
            #interrupt-cells = <2>;
            interrupt-controller;
            ngpios = <32>;
            status = "okay";
        };
        
        gpio1: gpio@40001000 {
            compatible = "vendor,gpio";
            reg = <0x40001000 0x1000>;
            interrupts = <0 41 4>;
            #gpio-cells = <2>;
            gpio-controller;
            #interrupt-cells = <2>;
            interrupt-controller;
            ngpios = <16>;
            status = "okay";
        };
    };
};

3.5 以太网控制器

/ {
    soc {
        eth0: ethernet@50000000 {
            compatible = "vendor,eth";
            reg = <0x50000000 0x1000>;
            interrupts = <0 50 4>;
            clocks = <&clk_eth>;
            phy-mode = "rgmii";
            phy-handle = <&phy0>;
            status = "okay";
            
            mdio {
                #address-cells = <1>;
                #size-cells = <0>;
                
                phy0: ethernet-phy@0 {
                    reg = <0>;
                    reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
                    reset-assert-us = <10000>;
                    reset-deassert-us = <10000>;
                };
            };
        };
    };
};

4. 时钟和电源管理

4.1 时钟控制器

/ {
    clocks {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;
        
        osc24m: osc24m {
            compatible = "fixed-clock";
            #clock-cells = <0>;
            clock-frequency = <24000000>;
            clock-output-names = "osc24m";
        };
        
        clk_pll: pll@10010000 {
            compatible = "vendor,pll";
            reg = <0x10010000 0x1000>;
            #clock-cells = <1>;
            clocks = <&osc24m>;
            clock-output-names = "pll_cpu", "pll_ddr", "pll_periph";
        };
        
        clk_uart: uart_clk {
            compatible = "fixed-factor-clock";
            #clock-cells = <0>;
            clocks = <&clk_pll 2>;
            clock-div = <4>;
            clock-mult = <1>;
            clock-output-names = "uart_clk";
        };
    };
};

4.2 电源管理

/ {
    regulators {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <0>;
        
        reg_3v3: regulator@0 {
            compatible = "regulator-fixed";
            reg = <0>;
            regulator-name = "3V3";
            regulator-min-microvolt = <3300000>;
            regulator-max-microvolt = <3300000>;
            regulator-always-on;
        };
        
        reg_1v8: regulator@1 {
            compatible = "regulator-fixed";
            reg = <1>;
            regulator-name = "1V8";
            regulator-min-microvolt = <1800000>;
            regulator-max-microvolt = <1800000>;
            gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;
            enable-active-high;
        };
        
        vref_reg: regulator@2 {
            compatible = "regulator-fixed";
            reg = <2>;
            regulator-name = "vref";
            regulator-min-microvolt = <2500000>;
            regulator-max-microvolt = <2500000>;
        };
    };
};

5. 引脚控制(Pinctrl)

5.1 引脚复用配置

/ {
    soc {
        pinctrl: pinctrl@60000000 {
            compatible = "vendor,pinctrl";
            reg = <0x60000000 0x1000>;
            
            // UART0引脚配置
            uart0_pins: uart0-pins {
                pins = "PA0", "PA1";
                function = "uart0";
                bias-disable;
            };
            
            // I2C0引脚配置
            i2c0_pins: i2c0-pins {
                pins = "PA2", "PA3";
                function = "i2c0";
                bias-pull-up;
            };
            
            // SPI0引脚配置
            spi0_pins: spi0-pins {
                pins = "PA4", "PA5", "PA6", "PA7";
                function = "spi0";
                bias-disable;
            };
            
            // 以太网引脚配置
            eth_pins: eth-pins {
                pins = "PB0", "PB1", "PB2", "PB3", 
                       "PB4", "PB5", "PB6", "PB7",
                       "PB8", "PB9", "PB10", "PB11";
                function = "eth";
                drive-strength = <40>;
                bias-disable;
            };
            
            // LED引脚配置
            led_pins: led-pins {
                pins = "PC0", "PC1", "PC2";
                function = "gpio";
                bias-pull-up;
                output-high;
            };
        };
        
        // 在设备节点中引用pinctrl
        uart0: serial@10000000 {
            compatible = "vendor,uart";
            reg = <0x10000000 0x1000>;
            interrupts = <0 10 4>;
            clocks = <&clk_uart>;
            pinctrl-names = "default";
            pinctrl-0 = <&uart0_pins>;
            status = "okay";
        };
        
        i2c0: i2c@20000000 {
            compatible = "vendor,i2c";
            reg = <0x20000000 0x1000>;
            interrupts = <0 20 4>;
            clocks = <&clk_i2c>;
            pinctrl-names = "default";
            pinctrl-0 = <&i2c0_pins>;
            status = "okay";
        };
    };
};

6. 完整开发板实例

6.1 完整的开发板设备树

// vendor-board-v1.dts
/dts-v1/;

#include "vendor-soc.dtsi"  // 包含SoC通用定义

/ {
    model = "Vendor Development Board V1.0";
    compatible = "vendor,board-v1", "vendor,soc";
    
    // 内存配置
    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>; // 1GB RAM
    };
    
    // 保留内存
    reserved-memory {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;
        
        // CMA区域
        linux,cma {
            compatible = "shared-dma-pool";
            reusable;
            size = <0x10000000>; // 256MB
            alignment = <0x2000>;
            linux,cma-default;
        };
        
        // DSP专用内存
        dsp_reserved: dsp@88000000 {
            reg = <0x88000000 0x08000000>; // 128MB
            no-map;
        };
    };
    
    // 启动参数
    chosen {
        bootargs = "console=ttyS0,115200 earlycon root=/dev/mmcblk0p2 rootwait";
        stdout-path = "serial0:115200n8";
    };
    
    // 别名
    aliases {
        serial0 = &uart0;
        serial1 = &uart1;
        ethernet0 = &eth0;
        mmc0 = &sdhci0;
        spi0 = &spi0;
        i2c0 = &i2c0;
        i2c1 = &i2c1;
    };
    
    // LED定义
    leds {
        compatible = "gpio-leds";
        
        led-0 {
            label = "heartbeat";
            gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
            linux,default-trigger = "heartbeat";
            default-state = "off";
        };
        
        led-1 {
            label = "mmc0";
            gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
            linux,default-trigger = "mmc0";
            default-state = "off";
        };
        
        led-2 {
            label = "cpu";
            gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
            linux,default-trigger = "cpu0";
            default-state = "off";
        };
    };
    
    // 按键定义
    gpio-keys {
        compatible = "gpio-keys";
        
        button-0 {
            label = "User Button 0";
            gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_PROG1>;
        };
        
        button-1 {
            label = "User Button 1";
            gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_PROG2>;
        };
    };
    
    // 背光控制
    backlight: backlight {
        compatible = "pwm-backlight";
        pwms = <&pwm 0 50000 0>; // PWM通道0,50kHz
        brightness-levels = <0 4 8 16 32 64 128 255>;
        default-brightness-level = <6>;
        enable-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
    };
    
    // 音频编解码器
    sound {
        compatible = "simple-audio-card";
        simple-audio-card,name = "Vendor-Board-Sound";
        simple-audio-card,format = "i2s";
        simple-audio-card,mclk-fs = <256>;
        
        simple-audio-card,cpu {
            sound-dai = <&i2s0>;
        };
        
        simple-audio-card,codec {
            sound-dai = <&codec>;
        };
    };
};

// 启用/禁用具体设备
&uart0 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart0_pins>;
    status = "okay";
};

&uart1 {
    status = "disabled"; // 默认禁用UART1
};

&i2c0 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c0_pins>;
    status = "okay";
    
    // 扩展板上的设备
    expansion_eeprom: eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
        pagesize = <16>;
    };
};

&i2c1 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1_pins>;
    status = "okay";
    
    // 音频编解码器
    codec: wm8960@1a {
        compatible = "wlf,wm8960";
        reg = <0x1a>;
        #sound-dai-cells = <0>;
    };
};

&spi0 {
    pinctrl-names = "default";
    pinctrl-0 = <&spi0_pins>;
    status = "okay";
    
    flash@0 {
        compatible = "winbond,w25q128", "jedec,spi-nor";
        reg = <0>;
        spi-max-frequency = <50000000>;
        #address-cells = <1>;
        #size-cells = <1>;
        
        partition@0 {
            label = "bootloader";
            reg = <0x000000 0x100000>;
        };
        
        partition@100000 {
            label = "kernel";
            reg = <0x100000 0x500000>;
        };
        
        partition@600000 {
            label = "rootfs";
            reg = <0x600000 0xa00000>;
        };
    };
};

&eth0 {
    pinctrl-names = "default";
    pinctrl-0 = <&eth_pins>;
    phy-mode = "rgmii";
    status = "okay";
    
    fixed-link {
        speed = <1000>;
        full-duplex;
    };
};

&sdhci0 {
    pinctrl-names = "default";
    pinctrl-0 = <&sdhci0_pins>;
    bus-width = <4>;
    cd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
    wp-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
    status = "okay";
};

&usb0 {
    dr_mode = "host";
    status = "okay";
};

&pwm {
    pinctrl-names = "default";
    pinctrl-0 = <&pwm0_pins>;
    status = "okay";
};

7. 设备树驱动实例

7.1 解析设备树的驱动程序

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>

struct my_device_data {
    struct device *dev;
    void __iomem *regs;
    struct gpio_desc *reset_gpio;
    struct regulator *vcc_supply;
    int irq;
    u32 clock_frequency;
    const char *device_name;
};

static const struct of_device_id my_device_of_match[] = {
    { .compatible = "vendor,my-device" },
    { .compatible = "vendor,my-device-v2" },
    {},
};
MODULE_DEVICE_TABLE(of, my_device_of_match);

static int my_device_parse_dt(struct platform_device *pdev, 
                             struct my_device_data *data)
{
    struct device_node *np = pdev->dev.of_node;
    int ret;
    
    if (!np) {
        dev_err(&pdev->dev, "No device tree node found\n");
        return -EINVAL;
    }
    
    // 获取设备名称
    ret = of_property_read_string(np, "device-name", &data->device_name);
    if (ret) {
        data->device_name = "default";
    }
    
    // 获取时钟频率
    ret = of_property_read_u32(np, "clock-frequency", &data->clock_frequency);
    if (ret) {
        data->clock_frequency = 1000000; // 默认1MHz
    }
    
    // 获取GPIO
    data->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset", 
                                              GPIOD_OUT_HIGH);
    if (IS_ERR(data->reset_gpio)) {
        return PTR_ERR(data->reset_gpio);
    }
    
    // 获取电源
    data->vcc_supply = devm_regulator_get_optional(&pdev->dev, "vcc");
    if (IS_ERR(data->vcc_supply)) {
        if (PTR_ERR(data->vcc_supply) == -EPROBE_DEFER)
            return -EPROBE_DEFER;
        data->vcc_supply = NULL;
    }
    
    // 获取中断
    data->irq = platform_get_irq(pdev, 0);
    if (data->irq < 0) {
        dev_info(&pdev->dev, "No IRQ specified\n");
    }
    
    return 0;
}

static int my_device_probe(struct platform_device *pdev)
{
    struct my_device_data *data;
    int ret;
    
    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;
    
    data->dev = &pdev->dev;
    
    // 解析设备树
    ret = my_device_parse_dt(pdev, data);
    if (ret)
        return ret;
    
    // 映射寄存器
    data->regs = devm_platform_ioremap_resource(pdev, 0);
    if (IS_ERR(data->regs))
        return PTR_ERR(data->regs);
    
    // 使能电源
    if (data->vcc_supply) {
        ret = regulator_enable(data->vcc_supply);
        if (ret) {
            dev_err(&pdev->dev, "Failed to enable regulator\n");
            return ret;
        }
    }
    
    // 硬件复位
    if (data->reset_gpio) {
        gpiod_set_value_cansleep(data->reset_gpio, 0);
        msleep(10);
        gpiod_set_value_cansleep(data->reset_gpio, 1);
        msleep(100);
    }
    
    platform_set_drvdata(pdev, data);
    
    dev_info(&pdev->dev, "Device %s probed, clock: %u Hz\n",
             data->device_name, data->clock_frequency);
    
    return 0;
}

static int my_device_remove(struct platform_device *pdev)
{
    struct my_device_data *data = platform_get_drvdata(pdev);
    
    if (data->vcc_supply)
        regulator_disable(data->vcc_supply);
    
    return 0;
}

static struct platform_driver my_device_driver = {
    .probe = my_device_probe,
    .remove = my_device_remove,
    .driver = {
        .name = "my-device",
        .of_match_table = my_device_of_match,
    },
};

module_platform_driver(my_device_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Device Tree aware device driver");

8. 设备树编译和调试

8.1 编译设备树

# Makefile示例
DTC = dtc
DTS = vendor-board-v1.dts
DTB = vendor-board-v1.dtb

all: $(DTB)

$(DTB): $(DTS)
    $(DTC) -I dts -O dtb -o $(DTB) $(DTS)

clean:
    rm -f $(DTB)

# 内核中的编译
obj-y += vendor-board-v1.dtb

8.2 设备树调试命令

# 查看设备树
cat /proc/device-tree/model
ls /proc/device-tree/

# 使用dtc工具反编译
dtc -I fs /proc/device-tree

# 查看特定设备
find /proc/device-tree -name "*uart*"

# 内核启动参数添加设备树调试
bootargs="console=ttyS0,115200 earlycon devicetree=debug"

设备树是现代Linux嵌入式系统的核心组成部分,通过合理设计设备树结构,可以创建高度可移植和可维护的硬件描述。掌握设备树语法和驱动程序的设备树解析是嵌入式Linux开发的重要技能。