HardIRQ 和 softirq : http://www.it165.net/os/html/201211/3766.html

博文:http://blog.csdn.net/yin262/article/details/53994699

中断:http://blog.csdn.net/skyflying2012/article/details/7850674

1: 通过调用 request_irq api来注册指定中断号上的 irq_handler,flags可选是否是共享或者其他
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);
}

2:requst_irq 进一步调用 request_threaded_irq
desc = irq_to_desc(irq); // 获取该irq号对应的 irq_desc结构,并check相关的字段,注意到desc结构和irq号是一一对应的(采用基数树结构实现irq_desc_tree)
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); // 创建一个irqaction对象
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;

retval = __setup_irq(irq, desc, action); // 调用__setup_irq 安装action

3:__setup_irq函数负责安装一个irq,应该是代码的核心所在
/*
* Internal function to register an irqaction - typically used to
* allocate special interrupts that are part of the architecture.
*/
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)

Step1: 如果触发类型没有指定,那么使用默认的配置
Step2:检查中断是否被嵌套到其他的中断线程中
Step3:如果该中断提供了一个线程函数并且中断不被其他线程嵌套,那么就为该中断创建一个线程
/*
* Create a handler thread when a thread function is supplied
* and the interrupt does not nest into another interrupt
* thread.
*/
if (new->thread_fn && !nested) {
ret = setup_irq_thread(new, irq, false);
if (ret)
goto out_mput;
if (new->secondary) {
ret = setup_irq_thread(new->secondary, irq, true);
if (ret)
goto out_thread;
}
}
Step4:如果该中断号被设置为共享,那么必须满足下面的条件
(1)同一个IRQ上被注册的其他irq_handler都设置了共享,即IRQF_SHARED
(2)中断触发类型必须相同(level, edge, polarity)
(3)是否是ONESHOT类型也必须保持一致
(4)all handlers must agree on per-cpuness

/* add new interrupt at end of irq queue */
do {
/*
* Or all existing action->thread_mask bits,
* so we can find the next zero bit for this
* new action.
*/
thread_mask |= old->thread_mask;
old_ptr = &old->next;
old = *old_ptr;
} while (old);
shared = 1;
Step5:
Step6:设置和更新/proc/interrupts的显示
register_irq_proc(irq, desc);
new->dir = NULL;
register_handler_proc(irq, new);

4:内核提供了配置开关,可以开启强制irq线程
注意:部分中断是不能被线程化的,例如IPIs中断等,这些中断被标志为:IRQF_NO_THREAD,All interrupts marked IRQF_TIMER or IRQF_PER_CPU are automatically excluded from threading
#ifdef CONFIG_IRQ_FORCE_THREADING
__readmostly bool fore_threads

5:早期的中断和异常处理
https://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-2.html
arch/x86/kernel/head64.c,中断向量共256个(前32个为异常,后面的为中断编号:32~255)
#define NUM_EXCEPTION_VECTORS 32
在64位模式下我们IDT(中断描述符表)初始化

set_intr_gate(n, addr)
set_system_gate(n, addr)
set_system_intr_gate(n, addr)
set_trap_gate(n, addr)
set_task_gate(n, addr)

#ifdef CONFIG_X86_64
struct desc_ptr idt_descr __ro_after_init = {
.size = NR_VECTORS * 16 - 1,
.address = (unsigned long) idt_table,
};

asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
{

for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
set_intr_gate(i, early_idt_handler_array[i]); // 设置中断门,填充到idt_table里
load_idt((const struct desc_ptr *)&idt_descr);
}

static inline void _set_gate(int gate, unsigned type, void *addr,
unsigned dpl, unsigned ist, unsigned seg)
{
gate_desc s;

pack_gate(&s, type, (unsigned long)addr, dpl, ist, seg);
/*
* does not need to be atomic because it is only done once at
* setup time
*/
write_idt_entry(idt_table, gate, &s);
write_trace_idt_entry(gate, &s);
}

static inline void native_write_idt_entry(gate_desc *idt, int entry, const gate_desc *gate)
{
memcpy(&idt[entry], gate, sizeof(*gate));
}

如果是由中断门进入中断处理程序的,CPU会清除IF标志位,这样当当前中断处理程序执行时,CPU不会对其他的中断进行处理;
只有当当前的中断处理程序返回时,CPU才在iret指令执行时重新设置IF标志位。

默认情况下,内核栈(线程栈大小为)2个Page大小(32位体系结构上8KB,64位体系结构上16KB)
中断上下文的栈空间大小在禁用KASAN时大小是2个Page大小(和线程栈类似)
#define IRQ_STACK_ORDER (2 + KASAN_STACK_ORDER)
#define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER)

#define THREAD_SIZE_ORDER (2 + KASAN_STACK_ORDER)
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)

PerCPU的中断栈
#ifdef CONFIG_X86_64
per_cpu(irq_stack_ptr, cpu) =
per_cpu(irq_stack_union.irq_stack, cpu) +
IRQ_STACK_SIZE;
#endif

arch/x86/include/asm/irq.h:44:extern __visible unsigned int do_IRQ(struct pt_regs *regs);
arch/x86/kernel/irq_64.c:59: WARN_ONCE(1, "do_IRQ(): %s has overflown the kernel stack (cur:%Lx,sp:%lx,irq stk top-bottom:%Lx-%Lx,exception stk top-bottom:%Lx-%Lx)\n",
arch/x86/kernel/irq.c:208: * do_IRQ handles all normal device IRQ's (the special
arch/x86/kernel/irq.c:212:__visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
arch/x86/entry/entry_64.S:518: interrupt do_IRQ
arch/x86/entry/entry_32.S:655: call do_IRQ

do_IRQ函数是所有普通设备中断的入口
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
__visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc * desc;
/* high bit used in ret_from_ code */
unsigned vector = ~regs->orig_ax;

/*
* NB: Unlike exception entries, IRQ entries do not reliably
* handle context tracking in the low-level entry code. This is
* because syscall entries execute briefly with IRQs on before
* updating context tracking state, so we can take an IRQ from
* kernel mode with CONTEXT_USER. The low-level entry code only
* updates the context if we came from user mode, so we won't
* switch to CONTEXT_KERNEL. We'll fix that once the syscall
* code is cleaned up enough that we can cleanly defer enabling
* IRQs.
*/

entering_irq();

/* entering_irq() tells RCU that we're not quiescent. Check it. */
RCU_LOCKDEP_WARN(!rcu_is_watching(), "IRQ failed to wake up RCU");

desc = __this_cpu_read(vector_irq[vector]);

if (!handle_irq(desc, regs)) {
ack_APIC_irq();

if (desc != VECTOR_RETRIGGERED) {
pr_emerg_ratelimited("%s: %d.%d No irq handler for vector\n",
__func__, smp_processor_id(),
vector);
} else {
__this_cpu_write(vector_irq[vector], VECTOR_UNUSED);
}
}

exiting_irq();

set_irq_regs(old_regs);
return 1;
}

调用流程 do_IRQ --> handle_irq --> generic_handle_irq_desc --> desc->handle_irq(desc);

作业:
(1)irq_stack_union的含义是什么?
(2)desc数据结构是何时创建的?
答:
256个中断向量对应的irq_desc不是一次性初始化完的。
其中一条路径是:start_kernel --> early_irq_init --> alloc_descs --> irq_insert_desc
另外一条路径是: arch_setup_msi_irq --> irq_alloc_hwirq --> irq_alloc_hwirqs --> __irq_alloc_descs --> alloc_descs

Linux 中断处理的更多相关文章

  1. linux中断处理原理分析

    Tasklet作为一种新机制,显然可以承担更多的优点.正好这时候SMP越来越火了,因此又在tasklet中加入了SMP机制,保证同种中断只能在一个cpu上执行.在软中断时代,显然没有这种考虑.因此同一 ...

  2. Linux中断处理体系结构分析

    Linux中断处理体系结构分析(一) 异常,就是可以打断CPU正常运行流程的一些事情,比如外部中断.未定义指令.试图修改只读的数据.执行swi指令(Software Interrupt Instruc ...

  3. [国嵌攻略][119][Linux中断处理程序设计]

    裸机中断: 1.中断统一入口. 2.注册中断处理程序. 3.根据中断源编号,调用中断处理程序. Linux中断 1.在entry-armv.S中的_irq_svc是中断统一入口. 2.获取产生中断源的 ...

  4. linux中断处理上下部分

    一.linux中断处理为什么要分为上下部 1.1. 中断处理的上半部(top half,又叫顶半部)和处理的下半部(bottom half,又叫底半部) 1.1. linux中断处理不参与调度,故中断 ...

  5. Linux.中断处理.入口x86平台entry_32.S

    Linux.中断处理.入口x86平台entry_32.S Linux.中断处理.入口x86平台entry_32.S 在保护模式下处理器是通过中断号和IDTR找到中断处理程序的入口地址的.IDTR存的是 ...

  6. Linux 中断处理浅析

    最近在研究异步消息处理, 突然想起linux内核的中断处理, 里面由始至终都贯穿着"重要的事马上做, 不重要的事推后做"的异步处理思想. 于是整理一下~ 第一阶段--获取中断号 每 ...

  7. 【转】Linux中断处理学习笔记

    原文网址:http://www.cnblogs.com/GT_Andy/archive/2011/06/21/2086100.html 1.Linux中断的注册与释放: 在<linux/inte ...

  8. Linux中断处理(二)

    与Linux设备驱动中中断处理相关的首先是申请与释放IRQ的API request_irq()和free_irq(),request_irq()的原型为:int request_irq(unsigne ...

  9. Linux中断处理(一)

    最近在研究异步消息处理, 突然想起linux内核的中断处理, 里面由始至终都贯穿着"重要的事马上做, 不重要的事推后做"的异步处理思想. 于是整理一下~~第一阶段--获取中断号每个 ...

  10. Linux中断处理驱动程序编写【转】

    转自:http://blog.163.com/baosongliang@126/blog/static/1949357020132585316912/ 本章节我们一起来探讨一下Linux中的中断 中断 ...

随机推荐

  1. webservice用cxf发布REST

    1.新建一个java项目 2.导入cxf相关的jar包,并部署到项目中 3.bean类 package com.xiaostudy; import javax.xml.bind.annotation. ...

  2. Solr学习总结 Solr的安装与配置

    接着前一篇,这里总结下Solr的安装与配置 1.准备 1.安装Java8 和 Tomcat9 ,java和tomcat 的安装这里不再重复.需要注意的是这两个的版本兼容问题.貌似java8 不支持,t ...

  3. 如何停止你的Streaming Application

    Spark 1.3及其前的版本 你的一个 spark streaming application 已经好好运行了一段时间了,这个时候你因为某种原因要停止它.你应该怎么做?直接暴力 kill 该 app ...

  4. Nordic老版官网介绍(2018-11-30停止更新)

    1. Nordic官网及资料下载 Nordic官网主页:https://www.nordicsemi.com/,进入官网后,一般点击“Products”标签页,即进入Nordic产品下载首页,其独立链 ...

  5. 一个不错的JavaScript解析浏览器路径方法

    JavaScript中有时需要用到当前的请求路径等涉及到url的情况,正常情况下我们可以使用location对象来获取我们需要的信息,本文从另外一个途径来解决这个问题,而且更加巧妙 方法如下: fun ...

  6. Java中如何获取多维数组的长度

    在程序处理中遍历数组是一种很常见的操作,但遍历数组时,我们需要知道数组各维度的长度(一维数组除外,for each 可以遍历),下面举例说明如何求多维数组的长度. 一维 : int [] array ...

  7. POJ 1160 经典区间dp/四边形优化

    链接http://poj.org/problem?id=1160 很好的一个题,涉及到了以前老师说过的一个题目,可惜没往那上面想. 题意,给出N个城镇的地址,他们在一条直线上,现在要选择P个城镇建立邮 ...

  8. Back Track5学习笔记

    1.BT5默认用户名:root.密码:toor(公司是yeslabccies) 2.进入图形化界面命令:startx 3.更改密码:sudo passwd root 扫描工具 第一部分网络配置: 4. ...

  9. RabbitMQ(4) 未路由的消息、TTL和死信

    未路由的消息 当生产这发送的消息到达指定的交换器后,如果交换器无法根据自身类型.绑定的队列以及消息的路由键找到匹配的队列,默认情况下消息将被丢弃.可以通过两种方式 处理这种情况,一是在发送是设置man ...

  10. log4cpp总结

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...