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. Asp.Net将Session保存在数据库中

    1.由于项目dll文件变动比较频繁,而保存登陆的状态又保存在Session中,所以导致用户经常无故掉线.(dll变动的时候导致Session丢失) 2.有一种方法可以长期保存session,那就是se ...

  2. 前端项目,引入PingFang SC字体

    在仿苹果官网"垃圾桶"时, 设计出的UI使用PingFang SC 字体,在网上查了很久,特记录.如果你有更好的方法,欢迎评论留言~ 实现原理,使用@font-face将字体下载在 ...

  3. python脚本6_打印菱形

    #输入菱形最宽的行所在的行数,打印菱形 m = int(input(">>>")) for n in range(m): print(" "* ...

  4. 输入T,返回TResult的委托

    下面的 委托 兼容输入 参数T,并且 返回值类型为TResult 的 方法(即封装一个具有一个参数并返回TResult 参数指定的类型值的方法) public delegate TResult Fun ...

  5. python 数组中如何根据值,获取索引,如何根据索引删除值 , 以及如何根据值删除值

    假设有一数组 s = [1,2,3,4,5,6,7,8,9] (1)如何根据值获取索引 ,如果值为5 , 那对应的索引为? (2)如何根据索引删除值 , 删除数组中索引5对应的值: (3)根据数组中的 ...

  6. less开发指南(一)- 小牛试刀

    [一]less简介 LESS(是.less后缀名的文件) 包含一套自定义的语法及一个解析器,我们根据这些语法定义自己的样式规则,这些规则最终会通过解析器,编译生成对应的 CSS 文件.LESS 并没有 ...

  7. 纯css做三角形形状

    /* create an arrow that points up */ div.arrow-up { width:0px; height:0px; border-left:5px solid tra ...

  8. IOS - 前台时的推送弹窗效果

    作者:Pikacode 原文链接:http://www.jianshu.com/p/67864e1c2085 本文获作者授权转载 或许很多童鞋还不知道,在 iOS 中收到推送通知时,如果 App 处于 ...

  9. python 黑客书籍 ——扫描+暴力破解

    https://legacy.gitbook.com/book/germey/net-security/details 网络安全 介绍 构建一个端口扫描器 利用Pexpect模拟SSH连接 利用Pxs ...

  10. Find the odd int

    Given an array, find the int that appears an odd number of times. There will always be only one inte ...