do_IRQ() 函数首先通过IRQ号获取到其对应的 irq_desc_t 结构,注意的是同一个中断有可能发生多次,所以要判断当前IRQ是否正在被处理当中(判断 irq_desc_t 结构的 status 字段是否设置了 IRQ_INPROGRESS 标志),如果不是处理当前,那么就获取到 action 链表,然后通过调用 handle_IRQ_event() 函数来执行 action 链表中的中断处理函数 。
如果在处理中断的过程中又发生了相同的中断(irq_desc_t 结构的 status 字段被设置了 IRQ_INPROGRESS 标志),那么就继续对中断进行处理 。处理完中断后,调用 do_softirq() 函数来对中断下半部进行处理(下面会说) 。
接下来看看 handle_IRQ_event() 函数的实现:
int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action){ int status; int cpu = smp_processor_id(); irq_enter(cpu, irq); status = 1; /* Force the "do bottom halves" bit */ if (!(action->flags & SA_INTERRUPT)) // 如果中断处理能够在打开中断的情况下执行, 那么就打开中断 __sti(); do { status |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); __cli(); irq_exit(cpu, irq); return status;}handle_IRQ_event() 函数非常简单,就是遍历 action 链表并且执行其中的处理函数,比如对于 时钟中断 就是调用 timer_interrupt() 函数 。这里要注意的是,如果中断处理过程能够开启中断的,那么就把中断打开(因为CPU接收到中断信号时会关闭中断) 。
中断处理 - 下半部(软中断)由于中断处理一般在关闭中断的情况下执行,所以中断处理不能太耗时,否则后续发生的中断就不能实时地被处理 。鉴于这个原因,Linux把中断处理分为两个部分,上半部 和 下半部,上半部 在前面已经介绍过,接下来就介绍一下 下半部 的执行 。
一般中断 上半部 只会做一些最基础的操作(比如从网卡中复制数据到缓存中),然后对要执行的中断 下半部 进行标识,标识完调用 do_softirq() 函数进行处理 。
softirq机制中断下半部 由 softirq(软中断) 机制来实现的,在Linux内核中,有一个名为 softirq_vec 的数组,如下:
static struct softirq_action softirq_vec[32];其类型为 softirq_action 结构,定义如下:
struct softirq_action{ void (*action)(struct softirq_action *); void *data;};softirq_vec 数组是 softirq 机制的核心,softirq_vec 数组每个元素代表一种softirq 。但在Linux中只定义了四种softirq,如下:
enum{ HI_SOFTIRQ=0, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, TASKLET_SOFTIRQ};HI_SOFTIRQ 是高优先级tasklet,而 TASKLET_SOFTIRQ 是普通tasklet,tasklet是基于softirq机制的一种任务队列(下面会介绍) 。NET_TX_SOFTIRQ 和 NET_RX_SOFTIRQ 特定用于网络子模块的软中断(不作介绍) 。
注册softirq处理函数要注册一个softirq处理函数,可以通过 open_softirq() 函数来进行,代码如下:
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data){ unsigned long flags; int i; spin_lock_irqsave(&softirq_mask_lock, flags); softirq_vec[nr].data = https://www.isolves.com/it/rj/czxt/linux/2019-11-27/data; softirq_vec[nr].action = action; for (i=0; i
Linux在系统初始化时注册了两种softirq处理函数,分别为 TASKLET_SOFTIRQ 和 HI_SOFTIRQ:
void __init softirq_init(){ ... open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);}处理softirq处理softirq是通过 do_softirq() 函数实现,代码如下:
asmlinkage void do_softirq(){ int cpu = smp_processor_id(); __u32 active, mask; if (in_interrupt()) return; local_bh_disable(); local_irq_disable(); mask = softirq_mask(cpu); active = softirq_active(cpu) & mask; if (active) { struct softirq_action *h;restart: softirq_active(cpu) &= ~active; local_irq_enable(); h = softirq_vec; mask &= ~active; do { if (active & 1) h->action(h); h++; active >>= 1; } while (active); local_irq_disable(); active = softirq_active(cpu); if ((active &= mask) != 0) goto retry; } local_bh_enable(); return;retry: goto restart;}前面说了 softirq_vec 数组有32个元素,每个元素对应一种类型的softirq,那么Linux怎么知道哪种softirq需要被执行呢?在Linux中,每个CPU都有一个类型为 irq_cpustat_t 结构的变量,irq_cpustat_t 结构定义如下:
typedef struct { unsigned int __softirq_active; unsigned int __softirq_mask; ...} irq_cpustat_t;其中 __softirq_active 字段表示有哪种softirq触发了(int类型有32个位,每一个位代表一种softirq),而 __softirq_mask 字段表示哪种softirq被屏蔽了 。Linux通过 __softirq_active 这个字段得知哪种softirq需要执行(只需要把对应位设置为1) 。
推荐阅读
- 终于知道为什么黑客学习过程首选的系统是Linux系统而不是windows
- Java开发必会的Linux命令
- Linux中zip压缩和unzip解压缩命令详解
- 旧婚纱怎么处理 白色婚纱怎么洗白
- 马桶晃动修复教程,马桶开胶晃动怎么处理
- 任雪案矿长怎么处理 害死任雪的矿长死了没
- 什么程度才算精通 Linux?
- Linux 磁盘的手动挂载和自动挂载实现详解
- linux添加账户、修改密码命令
- linux SSH的几种用法
