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. POJ - 3169 差分约束

    题意:n头牛,按照编号从左到右排列,两头牛可能在一起,接着有一些关系表示第a头牛与第b头牛相隔最多与最少的距离,最后求出第一头牛与最后一头牛的最大距离是多少,如         果最大距离无限大则输出 ...

  2. rsync | scp文件同步命令使用

    现在有一台服务器A,目录/data2/abc下存在若干文件夹和文件,需要复制到服务器B中.这时,可以在服务器A上执行rsync或者scp命令,将文件夹或文件复制到服务器B中. SCP: scp /da ...

  3. PAT1070. Mooncake (25)

    #include #include #include <stdio.h> #include <math.h> using namespace std; struct SS{ d ...

  4. Java高级面试题

    一.基础知识: 1)集合类:List和Set比较,各自的子类比较(ArrayList,Vector,LinkedList:HashSet,TreeSet): 2)HashMap的底层实现,之后会问Co ...

  5. python find命令、startwith命令

    python的字符串有很多好用的操作,比如find,startswith命令. 这几个命令在处理配置文件的时候很有用,比如用startswith判断是否是注释行. 注意:几个函数的返回值是不同滴. 函 ...

  6. Mysql建表好的例子

    1. DROP TABLE IF EXISTS `sys_warehouse_area`;CREATE TABLE `sys_warehouse_area` ( `id` bigint(20) NOT ...

  7. A4纸网页打印 html网页页面的宽度设置成多少

    A4纸竖向打印,html网页页面的宽度设置成多少?这个问题是我们大家所疑惑的,于是网上搜集整理下,希望可以帮助你们 最近开发项目时遇到了网页打印的问题,这是问题之二,打印宽度设置 在公制长度单位与屏幕 ...

  8. 在activity之间传递数据

    在activity之间传递数据 一.简介 二.通过intent传递数据 1.在需要传数据的界面调用 intent.putExtra("data1", "我是fry&quo ...

  9. Java的序列化机制

    1. 所有实现序列化的类都必须实现Serializable接口,序列化有如下两个特点: 如果一个类可以被序列化,那么它的子类也可以被序列化 由于static代表类成员,trasient代表对象的临时数 ...

  10. mysql学习之旅

    1,mysql安装脚本 #!/bin/bash RSYNC='10.39.2.120::wenhui/database/install' CMAKE='cmake-2.8.6.tar.gz' MYSQ ...