Linux中断

目录

中断

根据中断来源

内部中断

外部中断

根据中断是否可屏蔽

可屏蔽中断

不可屏蔽中断

中断入口跳转方法

向量中断

非向量中断

GIC(Generic Interrupt Controller)

SGI(Software Generated Interrupt)

PPI(Private Peripheral Interrupt)

SPI(Shared Peripheral Interrupt)

Linux中断处理程序架构​

顶半部(Top Half)

底半部(Bottom Half)

Linux中断编程

申请和释放中断

request_irq()

free_irq() 

使能和屏蔽中断

屏蔽中断

使能中断

低半部机制

tasklet

工作队列

软中断

线程化irq(threaded_irq)

中断共享


由于中断服务程序的执行并不存在于进程上下文中, 所以要求中断服务程序的时间要尽量短。 因此, Linux在中断处理中引入了顶半部和底半部分离的机制。
 

中断

根据中断来源

内部中断

       CPU内部(软件中断指令、 溢出、 除法错误等, 例如, 操作系统从用户态切换到内核态需借助CPU内部的软件中断)。

外部中断

       来自CPU外部, 由外设提出请求。

 

根据中断是否可屏蔽

可屏蔽中断

       可屏蔽中断可以通过设置中断控制器寄存器等方法被屏蔽, 屏蔽后, 该中断不再得到响应

不可屏蔽中断

       不可屏蔽中断不能被屏蔽

 

中断入口跳转方法

向量中断

采用向量中断的CPU通常为不同的中断分配不同的中断号, 当检测到某中断号的中断到来后, 就自动跳转到与该中断号对应的地址执行。不同中断号的中断有不同的入口地址。
 

非向量中断
 

非向量中断的多个中断共享一个入口地址, 进入该入口地址后, 再通过软件判断中断标志来识别具体是哪个中断。也就是说, 向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务程序入口地址。
 

GIC(Generic Interrupt Controller)

ARM多核处理器里最常用的中断控制器是GIC(Generic Interrupt Controller)

支持中断类型:

SGI(Software Generated Interrupt)

软件产生的中断, 可以用于多核的核间通信, 一个CPU可以通过写GIC的寄存器给另外一个CPU产生中断。 多核调度用的IPI_WAKEUP、 IPI_TIMER、IPI_RESCHEDULE、 IPI_CALL_FUNC、 IPI_CALL_FUNC_SINGLE、 IPI_CPU_STOP、 IPI_IRQ_WORK、IPI_COMPLETION都是由SGI产生的。
 

PPI(Private Peripheral Interrupt)

某个CPU私有外设的中断, 这类外设的中断只能发给绑定的那个CPU


SPI(Shared Peripheral Interrupt)

共享外设的中断, 这类外设的中断可以路由到任何一个CPU。
 

 

Linux中断处理程序架构

顶半部(Top Half)

顶半部用于完成尽量少的比较紧急的功能, 它往往只是简单地读取寄存器中的中断状态, 并在清除中断标志后就进行“登记中断”的工作。 “登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。 这样, 顶半部执行的速度就会很快, 从而可以服务更多的中断请求。
 

 

底半部(Bottom Half)
 

底半部几乎做了中断处理程序所有的事情, 而且可以被新的中断打断, 这也是底半部和顶半部的最大不同, 因为顶半部往往被设计成不可中断。 底半部相对来说并不是非常紧急的, 而且相对比较耗时, 不在硬件中断服务程序中执行。
 

Linux中断编程

申请和释放中断

request_irq()

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);

irq是要申请的硬件中断号。

handler是向系统登记的中断处理函数(顶半部) , 是一个回调函数, 中断发生时, 系统调用这个函数, dev参数将被传递给它。


irqflags是中断处理的属性, 可以指定中断的触发方式以及处理方式。 在触发方式方面, 可以是
IRQF_TRIGGER_RISING、 IRQF_TRIGGER_FALLING、 IRQF_TRIGGER_HIGH、 IRQF_TRIGGER_LOW等。 在处理方式方面, 若设置了IRQF_SHARED, 则表示多个设备共享中断, dev是要传递给中断服务程序的私有数据, 一般设置为这个设备的设备结构体或者NULL。
 

int devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id); 
//申请内核管理的资源,不需要手动释放

free_irq() 

void free_irq(unsigned int irq,void *dev_id);

使能和屏蔽中断

屏蔽中断

根据中断号屏蔽一个中断

void disable_irq(int irq);
void disable_irq_nosync(int irq);
void enable_irq(int irq);

屏蔽本CPU所有的中断:

#define local_irq_save(flags) ...
void local_irq_disable(void);

 

使能中断

恢复中断函数

#define local_irq_restore(flags) ...
void local_irq_enable(void);

低半部机制

        Linux实现底半部的机制主要有tasklet、 工作队列、 软中断和线程化irq。
 

tasklet

      它的执行上下文是软中断, 执行时机通常是顶半部返回的时候

void my_tasklet_func(unsigned long); /*定义一个处理函数*/
DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);
/*定义一个tasklet结构my_tasklet, 与my_tasklet_func(data)函数相关联*/

tasklet_schedule(&my_tasklet); //使系统在适当的时候进行调度

工作队列

工作队列的使用方法和tasklet非常相似, 但是工作队列的执行上下文是内核线程, 因此可以调度和睡眠

struct work_struct my_wq; /* 定义一个工作队列 */
void my_wq_func(struct work_struct *work); /* 定义一个处理函数 */
INIT_WORK(&my_wq, my_wq_func);
/* 初始化工作队列并将其与处理函数绑定 */
schedule_work(&my_wq); /* 调度工作队列执行 */

 

软中断

软中断(Softirq) 也是一种传统的底半部处理机制, 它的执行时机通常是顶半部返回的时候, tasklet是基于软中断实现的, 因此也运行于软中断上下文。


用softirq_action结构体表征一个软中断,这个结构体包含软中断处理函数指针和传递给该函数的参数。使用open_softirq() 函数可以注册软中断对应的处理函数, 而raise_softirq() 函数可以
触发一个软中断,软中断不允许睡眠

local_bh_disable() 和local_bh_enable() 是内核中用于禁止和使能软中断及tasklet底半部机制的函数。
 

软件中断:由软件触发的中断

软中断以及基于软中断的tasklet如果在某段时间内大量出现的话, 内核会把后续软中断放入ksoftirqd内核线程中执行。

总的来说, 中断优先级高于软中断, 软中断又高于任何一个线程。软中断适度线程化, 可以缓解高负载情况下系统的响应。
 

线程化irq(threaded_irq)

在内核中,还可以通过request_threaded_irq() 和devm_request_threaded_irq() 申请。 这两个函数的原型为:

int request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn,
unsigned long flags, const char *name, void *dev);

int devm_request_threaded_irq(struct device *dev, unsigned int irq,irq_handler_t handler, irq_handler_t thread_fn,unsigned long irqflags, const char *devname,void *dev_id);

会给每个中断申请一个中断的内核线程号

 

 

中断共享

1) 共享中断的多个设备在申请中断时, 都应该使用IRQF_SHARED标志, 而且一个设备以
IRQF_SHARED申请某中断成功的前提是该中断未被申请, 或该中断虽然被申请了, 但是之前申请该中断的所有设备也都以IRQF_SHARED标志申请该中断。

2) 尽管内核模块可访问的全局地址都可以作为request_irq(…, void*dev_id) 的最后一个参数dev_id, 但是设备结构体指针显然是可传入的最佳参数。


3) 在中断到来时, 会遍历执行共享此中断的所有中断处理程序, 直到某一个函数返回
IRQ_HANDLED。 在中断处理程序顶半部中, 应根据硬件寄存器中的信息比照传入的dev_id参数迅速地判断是否为本设备的中断, 若不是, 应迅速返回IRQ_NONE
 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值