linux查看sirq队列长度,Linux Interrupt

在面试的时候我们常常被问及一个问题:几种中断下半部机制 softirq、tasklet、workqueue 有什么区别?Linux 为什么要设计这几种机制?真正能够回答清楚的人还是少数的。下面我们就详细分析一下这其中的区别。本文的代码分析基于 Linux kernel 3.18.22 和 arm64 架构,最好的学习方法还是 “RTFSC”

1. Linux 中断

arm64 和其他所有 CPU 架构的中断处理流程都是一样:正常执行流程被打断进入中断服务程序,保护现场、处理中断、恢复现场:

在整个中断处理过程中,arm64 的 CPU 全局中断是自动 disable 的 (PSTATE 寄存器中的 interrupt bit 被 masks)。如果用户想支持 interrupt nested,需要自己在中断服务程序中使能中断。Linux 现在是不使用中断嵌套的。

1.1 CPU 中断打开 / 关闭

arm64 关闭和打开本地 CPU 的全局中断的方法,是操作 SPSR(Saved Process Status Register) 寄存器 IRQ mask bit。

Linux 中 arm64 关闭和打开本地 CPU 中断的函数实现。arch/arm64/include/asm/irqflags.h:

local_irq_disable() -> raw_local_irq_disable() -> arch_local_irq_disable()

local_irq_enable() -> raw_local_irq_enable() -> arch_local_irq_enable()

static inline void arch_local_irq_enable(void)

{

asm volatile(

// (1) 清除 DAIF 中的 bit2 I 标志位,打开中断"msrdaifclr, #2// arch_local_irq_enable"

:

:

: "memory");

}

static inline void arch_local_irq_disable(void)

{

asm volatile(

// (2) 设置 DAIF 中的 bit2 I 标志位,关闭中断"msrdaifset, #2// arch_local_irq_disable"

:

:

: "memory");

}

static inline unsigned long arch_local_irq_save(void)

{

unsigned long flags;

asm volatile(

// (3) 备份 DAIF 标志"mrs%0, daif// arch_local_irq_save\n"

"msrdaifset, #2"

: "=r" (flags)

:

: "memory");

return flags;

}

static inline unsigned long arch_local_save_flags(void)

{

unsigned long flags;

asm volatile(

// (4) 恢复 DAIF 标志"mrs%0, daif// arch_local_save_flags"

: "=r" (flags)

:

: "memory");

return flags;

}

1.2 中断控制器 GIC

上面描述了 CPU 对全局中断的处理,但是还有一个工作需要有人去做:就是把外部中断、内部中断、CPU 间中断等各种中断按照优先级、亲和力、私有性等发送给多个 CPU。负责这个工作的就是中断控制器 GIC(Generic Interrupt Controller)。

从软件角度上看,GIC 可以分成两个功能模块:Distributor。负责连接系统中所有的中断源,通过寄存器可以独立的配置每个中断的属性:priority、state、security、outing information、enable status。定义哪些中断可以转发到 CPU core。

CPU Interface。CPU core 用来接收中断,寄存器主要提供的功能:mask、 identify 、control states of interrupts forwarded to that core。每个 CPU core 拥有自己的 CPU interface。

对 GIC 来说,中断可以分成以下几种类型:SGI(Software Generated Interrupt),Interrupt IDs 0-15。系统一般用其来实现 IPI 中断。

PPI(Private Peripheral Interrupt),Interrupt IDs16-31。私有中断,这种中断对每个 CPU 都是独立一份的,比如 per-core timer 中断。

SPI(Shared Peripheral Interrupt),Interrupt numbers 32-1020。最常用的外设中断,中断可以发给一个或者多个 CPU。

LPI(Locality-specific Peripheral Interrupt)。基于 message 的中断,GICv2 和 GICv1 中不支持。

GIC 从原理上理解并不难,但是如果涉及到级联等技术细节,整个初始化过程还是比较复杂。大家可以自行下载 GIC 手册:GIC-400、GIC-500 学习,GIC 代码分析 也是一篇很不错的分析文章。

一款 GIC 相关的操作函数都会集中到 irq_chip 数据结构中,以 GIC-400 为例,它的相关操作函数如下:drivers/irqchip/irq-gic.c:

static struct irq_chip gic_chip = {

.name= "GIC",

.irq_mask= gic_mask_irq,

.irq_unmask= gic_unmask_irq,

.irq_eoi= gic_eoi_irq,

.irq_set_type= gic_set_type,

.irq_retrigger= gic_retrigger,

#ifdef CONFIG_SMP.irq_set_affinity= gic_set_affinity,

#endif.irq_set_wake= gic_set_wake,

};

1.3 Linux 中断处理流程

从代码上看 Linux 中断的处理流程大概是这样的:

9629c21646a575d6ca0b643f96613d04.png

从处理流程上看,对于 gic 的每个中断源,Linux 系统分配一个 irq_desc 数据结构与之对应。irq_desc 结构中有两个中断处理函数 desc->handle_irq() 和 desc->action->handler(),这两个函数代表中断处理的两个层级:desc->handle_irq()。第一层次的中断处理函数,这个是系统在初始化时根据中断源的特征统一分配的,不同类型的中断源的 gic 操作是不一样的,把这些通用 gic 操作提取出来就是第一层次的操作函数。具体实现包括: handle_fasteoi_irq()

handle_simple_irq()

handle_edge_irq()

handle_level_irq()

handle_percpu_irq()

handle_percpu_devid_irq()

desc->action->handler() 第二层次的中断处理函数,由用户注册实现具体设备的驱动服务程序,都是和 GIC 操作无关的代码。同时一个中断源可以多个设备共享,所以一个 desc 可以挂载多个 action,由链表结构组织起来。

46406196dfb6125162456539f8384942.png

1.4 中断服务注册

从上一节的中断二层结构中可以看到第二层的中断处理函数 desc->action->handler 是由用户来注册的,下面我们来分析具体注册过程:kernel/irq/manage.c:

request_irq() -> request_threaded_irq() -> __setup_irq()

static inline int __must_check

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

const char *name, void *dev)

{

return request_threaded_irq(irq, handler, NULL, flags, name, dev);

}

| →

int request_threaded_irq(unsigned int irq, irq_handler_t handler,

irq_handler_t thread_fn, unsigned long irqflags,

const char *devname, void *dev_id)

{

struct irqaction *action;

struct irq_desc *desc;

int retval;

/*

* Sanity-check: shared interrupts must pass in a real dev-ID,

* otherwise we'll have trouble later trying to figure out

* which interrupt is which (messes up the interrupt freeing

* logic etc).

*/

if ((irqflags & IRQF_SHARED) && !dev_id)

return -EINVAL;

// (1) 根据中断号找到对应的 desc 结构desc = irq_to_desc(irq);

if (!desc)

return -EINVAL;

if (!irq_settings_can_request(desc) ||

WARN_ON(irq_settings_is_per_cpu_devid(desc)))

return -EINVAL;

// (2) 如果 action->handler 为空,那么用户是想创建一个线程化中断// 将线程化中断的 action->handler 初始化为 irq_default_primary_handler()// irq_default_primary_handler() 非常简单,只是返回一个 IRQ_WAKE_THREAD 值if (!handler) {

if (!thread_fn)

return -EINVAL;

handler = irq_default_primary_handler;

}

// (3) 分配新的 action 数据结构action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);

if (!action)

return -ENOMEM;

action->handler = handler;

action->thread_fn = thread_fn;

action->flags = irqflags;

action->name = devname;

action->dev_id = dev_id;

chip_bus_lock(desc);

// (4) 将新的 action 结构安装到 desc 中retval = __setup_irq(irq, desc, action);

chip_bus_sync_unlock(desc);

if (retval)

kfree(action);

#ifdef CONFIG_DEBUG_SHIRQ_FIXMEif (!retval && (irqflags & IRQF_SHARED)) {

/*

* It's a shared IRQ -- the driver ought to be prepared for it

* to happen immediately, so let's make sure....

* We disable the irq to m

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值