天天看点

关于linux中断的一些记述

1. linux不支持中断优先级

2. linux默认不支持中断嵌套,老版本支持嵌套(不同类型中断可以嵌套,但是同类型中断需要依次处理),参考 Linux的中断可以嵌套吗?

老版本中断处理流程:

中断发生------> (此时中断关闭)中断门 ------> ... ------>handle_IRQ_event (中断打开(可选))------> irqaction(执行驱动注册的中断处理函数)

新版本中去除了handle_IRQ_event后打开中断的操作,可以防止嵌套过多导致栈溢出.

3. 中断产生到处理(不同架构上可能有所不同x86和mips上应该是这样吧)

  • 设备产生中断,设置中断标志位(pending位),并向中断控制器发送中断请求;
  • 中断控制器(可以全局屏蔽中断请求,也可以单独屏蔽某一请求)将通过中断控制器到cpu的中断线通知cpu有中断,然后通过总线告知cpu此次中断的中断号;
  • cpu硬件修改CPSR(Current Program Status Register)寄存器,切换工作模式为IRQ模式;保存CPSR值(上一步修改模式之前的值)和PC值 ;通过写寄存器位"CPSR.I = 1",禁用本地cpu中断;设定PC值为IRQ exception vector,ARM处理器就会跳转到IRQ的exception vector地址.(参考 Linux kernel的中断子系统之(六):ARM中断处理过程)
  • 之后软件处理(balabala...),根据中断号跳转到相应的中断服务程序(这边内核要做一些工作,balabala...);
  • 对于几个设备共享的中断,依次执行irqaction链表上驱动注册的中断处理函数
  • 由于是共享中断不清楚到底是哪个设备产生的,因此每个中断处理函数需要判断是不是自己设备产生的中断,方法是查询设备的中断标志位(pending位)
  • 如果是自己设备产生的中断,那么就清除中断标志位(目的是使设备能够继续工作产生下一次中断),接着执行服务.

4. HW interrupt id到irq的映射

(图片出处 Linux kernel的中断子系统之(一):综述)

关于linux中断的一些记述

从硬件层面上讲,中断由三个部分参与:

  • 中断的制造者(中断源):能够产生中断的设备
  • 中断的管理者:中断控制器(interrupt controller)
  • 中断的处理者:cpu

能够产生中断的设备的interrupt request line连接到某一interrupt controller上;interrupt controller对于它管理的设备中断请求进行屏蔽,优先级等的管理,然后将中断请求发送给上级interrupt controller或者cpu.

为了能够处理尽可能多的外部中断,可以增加interrupt controllers的数量来实现,Interrupt controllers通过级联的方式实现扩展,其中root interrupt controller负责转发其它interrupt controller的interrupt requset给cpu.

linux系统中为了方便管理中断,采用虚拟的irq(软件中断号)来标示某一中断,每个interrupt controller管理的中断对应这一个irq domain.irq通过某一关系map到HW interrupt id上(譬如线性映射).大体流程(参考 Linux kernel的中断子系统之(二):IRQ Domain介绍):

  1. 初始化interrupt controllers和irq-domain,每个interrupt controller对应一个irq domain
  2. 设备树初始化过程中建立irq <-----> HW interrupt id的map关系;没有设备树,采用BSP(板级支持包)的话就在BSP中建立map关系
  3. 设备驱动通过"int platform_get_irq(struct platform_device *, unsigned int);"来获取irq
    int platform_get_irq(struct platform_device *, unsigned int);
               
    因为我们的设备树中描述了整个中断子系统的层级关系,因此设备树初始化过程中可以构建出irq <-----> HW interrupt id的map关系,而且当前设备的设备树节点中描述了它所引用的中断属性(HW interrupt id,interrupt controller,trigger level等),所以当使用platform_get_irq时系统会根据当前设备节点的描述结合map表得到设备树节点中描述的HW interrupt id对应的irq;对于BSP,由BSP工程师构建map表,并设备驱动对应的BSP设备文件的platform_device结构的resorce字段中指定irq(这里直接指定irq不是HW interrupt id).

5. 常用接口

注册释放接口:

static inline int __must_check
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);
           
开关接口(屏蔽非屏蔽):
           
/*本地 CPU 全局中断的开关*/
/*local_irq_save/restore可以保存和回复中断的开关状态*/
/*local_irq_enable/disable则忽视了本地cpu之前的状态*/
#define local_irq_enable()	do { raw_local_irq_enable(); } while (0)
#define local_irq_disable()	do { raw_local_irq_disable(); } while (0)
#define local_irq_save(flags)	do { raw_local_irq_save(flags); } while (0)
#define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0)

/*单个中断源中断的开关*/
void enable_irq(unsigned int irq);
void disable_irq(unsigned int irq);
           

6. 中断线程化:

一般的可以通过request_irq()来注册中断,通过request_threaded_irq()来注册线程化中断.线程化主要目的是把中断上下文的任务迁移到线程中,减少系统关中断的时间(linux默认中断不可嵌套,中断发生后到中断处理函数执行完毕过程中,中断是一直关闭的,上面有提到),增强系统响应中断的实时性.

中断对应的线程命名规则为:

t = kthread_create(irq_thread, new, "irq/%d-%s", irq, new->name);
           

我们可以看到形如下图中的线程名:

root@:/ # ps | grep "irq/"
root      171   2     0      0     irq_thread 0000000000 S irq/389-charger
root      239   2     0      0     irq_thread 0000000000 S irq/296-PS_int-
root      247   2     0      0     irq_thread 0000000000 S irq/297-1124000
root      1415  2     0      0     irq_thread 0000000000 S irq/293-goodix_
[email protected]:/ #

           

7.软中断,tasklet,workqueue(转自)

linux kernel的中断子系统之(八):softirq

linux kernel的中断子系统之(九):tasklet

参考文章:

Linux Interrupt

linux kernel的中断子系统之(七):GIC代码分析

中断子系统  (强烈推荐看一下)

继续阅读