linux中断(中断一)

  中断是操作系统中至关重要的机制,它能够显著提高系统的响应性能和并发处理能力。
  中断是指在 CPU 正常运行期间,由外部或内部事件引起的一种机制。当中断发生时,CPU会停止当前正在执行的程序,并转而执行触发该中断的中断处理程序。处理完中断处理程序后,CPU 会返回到中断发生的地方,继续执行被中断的程序。中断机制允许CPU在实时响应外部或内部事件的同时,保持对其他任务的处理能力。

一、中断的上下半部

  中断的执行需要快速响应,但并不是所有中断都能迅速完成。此外,Linux 中的中断不支持嵌套,意味着在正式处理中断之前会屏蔽其他中断,直到中断处理完成后再重新允许接收中断,如果中断处理时间过长,将会引发问题。
  而为了让系统可以更好地处理中断事件,提高实时性和响应能力,将中断服务程序划分为上下文两部分:
  中断上文是中断服务程序的第一部分,它主要处理一些紧急且需要快速响应的任务。中断上文的特点是执行时间较短,旨在尽快完成对中断的处理。这些任务可能包括保存寄存器状态、更新计数器等,以便在中断处理完成后能够正确地返回到中断前的执行位置。
  中断下文是中断服务程序的第二部分,它主要处理一些相对耗时的任务。由于中断上文需要尽快完成,因此中断下文负责处理那些不能立即完成的、需要更多时间的任务。这些任务可能包括复杂的计算、访问外部设备或进行长时间的数据处理等。

二、linux中断的API函数

2.1、request_irq

  request_irq 函数是在 Linux 内核中用于注册中断处理程序的函数。它用于请求一个中断号(IRQ number)并将一个中断处理程序与该中断关联起来。
在这里插入图片描述
  irq 参数用来指定要请求的中断号,中断号需要通过 gpio_to_irq 函数映射GPIO引脚来获得
  irq_handler_t handler 参数是一个函数指针,指向了中断处理程序的函数。中断处理程序是在中断事件发生时调用的函数,用于处理中断事件
  unsigned long flags:中断处理程序的标志位。这个参数用于指定中断处理程序的行为和属性,如中断触发方式、中断共享等。可以使用不同的标志位进行位运算来组合多个属性。常用的标志位包括:

IRQF_TRIGGER_NONE:无触发方式,表示中断不会被触发。
IRQF_TRIGGER_RISING:上升沿触发方式,表示中断在信号上升沿时触发。
IRQF_TRIGGER_FALLING:下降沿触发方式,表示中断在信号下降沿时触发。
IRQF_TRIGGER_HIGH:高电平触发方式,表示中断在信号为高电平时触发。
IRQF_TRIGGER_LOW:低电平触发方式,表示中断在信号为低电平时触发。
IRQF_SHARED:中断共享方式,表示中断可以被多个设备共享使用。

2.2、gpio_to_irq

  gpio_to_irq 函数用于将 GPIO 引脚的编号(GPIO pin number)转换为对应的中断请求号(interrupt request number)。
在这里插入图片描述

2.3、free_irq

  free_irq 函数用于释放之前通过 request_irq 函数注册的中断处理程序。它的作用是取消对中断的注册并释放相关的系统资源
在这里插入图片描述

2.4、中断服务函数

  中断处理程序是在中断事件发生时自动调用的函数。它负责处理与中断相关的操作,例如读取数据、清除中断标志、更新状态等。
irqreturn_t handler(int irq, void *dev_id) 是一个典型的中断服务函数的函数原型。
在这里插入图片描述
  在处理程序中,通常需要注意以下几个方面:
  (1)处理程序应该尽可能地快速执行,以避免中断丢失或过多占用CPU时间。
  (2)如果中断源是共享的,处理程序需要处理多个设备共享同一个中断的情况。
  (3)处理程序可能需要与其他部分的代码进行同步,例如访问共享数据结构或使用同步机制来保护临界区域。
  (4)处理程序可能需要与其他线程或进程进行通信,例如唤醒等待的线程或发送信号给其他进程。

三、代码示例

3.1、驱动层程序

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

#define GPIO_PIN 101

// 中断处理函数
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
    
    
    printk(KERN_INFO "Interrupt occurred on GPIO %d\n", GPIO_PIN);
    printk(KERN_INFO "This is irq_handler\n");
    return IRQ_HANDLED;
}

static int __init interrupt_init(void)
{
    
    
    int irq_num;

    printk(KERN_INFO "Initializing GPIO Interrupt Driver\n");

    // 将GPIO引脚映射到中断号
    irq_num = gpio_to_irq(GPIO_PIN);
    printk(KERN_INFO "GPIO %d mapped to IRQ %d\n", GPIO_PIN, irq_num);

    // 请求中断
    if (request_irq(irq_num, gpio_irq_handler, IRQF_TRIGGER_RISING, "irq_test", NULL) != 0) {
    
    
        printk(KERN_ERR "Failed to request IRQ %d\n", irq_num);

        // 请求中断失败,释放GPIO引脚
        gpio_free(GPIO_PIN);
        return -ENODEV;
    }
    return 0;
}

static void __exit interrupt_exit(void)
{
    
    
    int irq_num = gpio_to_irq(GPIO_PIN);

    // 释放中断
    free_irq(irq_num, NULL);
    printk(KERN_INFO "GPIO Interrupt Driver exited successfully\n");
}

module_init(interrupt_init);
module_exit(interrupt_exit);

3.2、linux中断使用API要点

    // 将GPIO引脚映射到中断号
    irq_num = gpio_to_irq(GPIO_PIN);
request_irq(irq_num, gpio_irq_handler, IRQF_TRIGGER_RISING, "irq_test", NULL)

猜你喜欢

转载自blog.csdn.net/xxxx123041/article/details/134062595