QEMU虚拟硬件中断编程

概述

本课程将指导您如何在QEMU虚拟环境中为PCI设备或平台设备编写简单的中断处理程序。中断是硬件与操作系统通信的重要机制,掌握中断编程对理解系统底层运作至关重要。

硬件设备 中断控制器 CPU 中断请求 中断信号

中断处理基础

在Linux内核中,中断处理涉及以下关键概念:

PCI设备中断处理示例

以下是一个简单的PCI设备中断处理程序示例:

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

// 假设我们的设备使用IRQ 11
#define MY_IRQ 11

// 中断处理函数
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    // 检查是否真的是我们的设备产生的中断
    if (irq != MY_IRQ) {
        return IRQ_NONE; // 不是我们的中断,立即返回
    }
    
    printk(KERN_INFO "中断已触发! IRQ: %d\n", irq);
    
    // 处理硬件中断...
    // 通常包括读取设备状态寄存器、清除中断标志等
    
    return IRQ_HANDLED; // 中断已处理
}

// 设备初始化函数
static int __init my_device_init(void)
{
    int ret;
    
    printk(KERN_INFO "初始化PCI设备驱动\n");
    
    // 注册中断处理程序
    ret = request_irq(MY_IRQ, my_interrupt_handler, 
                     IRQF_SHARED, "my_pci_device", NULL);
    
    if (ret) {
        printk(KERN_ERR "无法注册中断处理程序: %d\n", ret);
        return ret;
    }
    
    printk(KERN_INFO "中断处理程序注册成功,IRQ: %d\n", MY_IRQ);
    return 0;
}

// 设备清理函数
static void __exit my_device_exit(void)
{
    // 释放中断处理程序
    free_irq(MY_IRQ, NULL);
    
    printk(KERN_INFO "PCI设备驱动已卸载\n");
}

module_init(my_device_init);
module_exit(my_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("嵌入式系统课");
MODULE_DESCRIPTION("简单的PCI设备中断处理示例");

注意:在实际应用中,您需要根据具体硬件修改IRQ号码和处理逻辑。共享中断需要使用IRQF_SHARED标志,并且需要正确识别中断来源。

平台设备中断处理示例

对于平台设备,中断处理略有不同:

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

static int irq_number;

// 中断处理函数
static irqreturn_t my_platform_interrupt(int irq, void *dev_id)
{
    printk(KERN_INFO "平台设备中断已触发! IRQ: %d\n", irq);
    
    // 处理中断...
    
    return IRQ_HANDLED;
}

// 平台设备探测函数
static int my_platform_probe(struct platform_device *pdev)
{
    int ret;
    
    // 获取设备树中定义的中断
    irq_number = platform_get_irq(pdev, 0);
    if (irq_number < 0) {
        dev_err(&pdev->dev, "无法获取IRQ号码\n");
        return irq_number;
    }
    
    // 注册中断处理程序
    ret = devm_request_irq(&pdev->dev, irq_number, my_platform_interrupt,
                          0, "my_platform_device", NULL);
    
    if (ret) {
        dev_err(&pdev->dev, "无法注册中断: %d\n", ret);
        return ret;
    }
    
    dev_info(&pdev->dev, "平台设备中断处理程序注册成功,IRQ: %d\n", irq_number);
    return 0;
}

// 平台设备驱动结构
static struct platform_driver my_platform_driver = {
    .driver = {
        .name = "my_platform_device",
        .owner = THIS_MODULE,
    },
    .probe = my_platform_probe,
};

module_platform_driver(my_platform_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("嵌入式系统课");
MODULE_DESCRIPTION("简单的平台设备中断处理示例");

关键函数说明

函数 描述 参数说明
request_irq() 注册中断处理程序 IRQ号码、处理函数、标志、设备名、设备ID
free_irq() 释放中断处理程序 IRQ号码、设备ID
devm_request_irq() 设备资源管理版本的中断注册 设备指针、IRQ号码、处理函数、标志、设备名、设备ID
platform_get_irq() 从平台设备获取IRQ号码 平台设备指针、索引号

中断处理最佳实践

在QEMU中测试中断

在QEMU环境中测试中断处理程序,可以按照以下步骤:

  1. 编译并加载内核模块
  2. 查看系统日志确认中断处理程序已注册
  3. 通过QEMU monitor或设备模拟触发中断
  4. 观察系统日志中的中断处理信息
  5. 使用工具如cat /proc/interrupts查看中断统计
中断处理流程 1. 中断发生 2. 保存上下文 3. 执行处理程序 4. 恢复上下文

提示:在开发过程中,可以使用printk输出调试信息,但要注意在生产代码中减少使用,以免影响性能。