Linux中断底半部机制
参考:
为了提高系统的响应能力和并发能力,Linux将中断处理分了上半部和下半部。当一个中断产生,调用该中断对应的处理程序(上半部),然后告诉系统,对应的后半部可以执行了,中断处理程序立即返回,下半部会在合适的时机由操作系统调用。这样一来就大大的减少了中断处理所需要的时间。
中断上半部处理函数就是Linux中断体系结构中介绍的request_irq中注册的irq_handler_t类型的函数。
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
软中断可以使内核延期执行某个任务,他们的运作方式和具体的硬件类似,甚至可以说这里就是模拟的硬件中断,所以称之为软件中断也不为过。既然提到软中断,那么自然就设计到几个点:
- 软中断的注册
- 软中断的触发
- 软中断的处理
内核版本中定义了10个软中断,并且系统不建议用户自己添加软中断,所以对于软中断基本用于已定义好的功用,而如果用户需要,可以使用其中的一个类型即TASKLET_SOFTIRQ
具体的软中断类型如下:

1 enum
2 {
3 HI_SOFTIRQ=0,
4 TIMER_SOFTIRQ,
5 NET_TX_SOFTIRQ,
6 NET_RX_SOFTIRQ,
7 BLOCK_SOFTIRQ,
8 BLOCK_IOPOLL_SOFTIRQ,
9 TASKLET_SOFTIRQ,
10 SCHED_SOFTIRQ,
11 HRTIMER_SOFTIRQ,
12 RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
13
14 NR_SOFTIRQS
15 };

每个CPU维护一个软中断位图__softirq_pending,其实是一个32位的字段,每一位对应一个软中断。处理软中断时会获取当前CPU的软中断位图,根据各个位的设置,进行处理。
typedef struct {
unsigned int __softirq_pending;
unsigned int local_timer_irqs;
} ____cacheline_aligned irq_cpustat_t;
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
/* arch independent irq_stat fields */
#define local_softirq_pending() \
__IRQ_STAT(smp_processor_id(), __softirq_pending)
1、软中断的注册
软中断的核心机制是一张表,类似于IDT,包含32个softirq_vec结构,该结构很简单:就是一个函数地址,每个软中断对应其中的一个,所以现在也仅仅使用前10项。
struct softirq_action
{
void (*action)(struct softirq_action *);
void *data;
};
static struct softirq_action softirq_vec[] __cacheline_aligned_in_smp;
系统通过open_softirq函数注册一个软中断,具体就是在softirq_vec数组中根据中断号设置其对应的处理例程。
softirq_init中初始化了TASKLET相关的处理函数。
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
void __init softirq_init(void)
{
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
nr是上面的一个枚举值,action便是对应软中断的处理函数。软中段执行时taskelet_action是执行的入口函数。
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list; local_irq_disable();
list = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = NULL;
local_irq_enable(); while (list) {
struct tasklet_struct *t = list; list = list->next; if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
} local_irq_disable();
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
static void tasklet_action(struct softirq_action *a)
Linux系统通过raise_softirq函数引发一个软中断,每个CPU有个软中断位图,有32位,最多可对应32个软中断,当置位图对应位为1时,表明触发了对应的软中断。在下次系统检查是否有软中断时就会被检测得到,从而进行处理。

1 void raise_softirq(unsigned int nr)
2 {
3 unsigned long flags;
4
5 local_irq_save(flags);
6 raise_softirq_irqoff(nr);
7 local_irq_restore(flags);
8 }

对于tasklet,在tasklet_schedule()函数中会调用到raise_softirq。
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
void fastcall __tasklet_schedule(struct tasklet_struct *t)
-->struct tasklet_head per_cpu_tasklet_vec.list = t; //加入链表
-->raise_softirq_irqoff(TASKLET_SOFTIRQ);
-->__raise_softirq_irqoff(nr);
-->or_softirq_pending(1UL << (nr));
-->local_softirq_pending() |= (1UL << (nr))//即struct irq_cpustat_t irq_stat[0].__softirq_pending |= (1UL << (nr)
处理时机:
软中断大概在三个地方会被检测是否存在,如果存在会进行处理:
- 从一个硬件中断返回时
- 在ksoftirqd内核线程中
- 在那些显式检查和执行待处理的软中断的代码中
中断上下文:CPU处于处理中断上半部或者下半部,内核用in_interrupt来判断是否处于中断上下文。这是一个宏:
#define in_interrupt() (irq_count())
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK))
可以看到这里中断上下文包括硬件中断、软件中断、NMI中断。说到这里,出现了一个preempt_count(),LInux为每个进程的thread_info结构中维护了一个preempt_count字段,该字段是int型,因此有32位,用于支持内核抢占。当该字段为0的时候,表示当前允许内核抢占,否则不可以。具体请参考另一篇博文:Linux中的进程调度
处理过程:以从一个硬件中断返回时的情景为例分析,这也是tasklet的使用情景。
软中断的处理核心都在do_softirq函数。
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
-->irq_exit();//硬件中断返回时检测软中断
-->invoke_softirq();
-->asmlinkage void __do_softirq(void)
-->pending = local_softirq_pending();//获取挂起位
h = softirq_vec;//依次执行挂起位对应的处理函数
do {
if (pending & ) {
h->action(h);
rcu_bh_qsctr_inc(cpu);
}
h++;
pending >>= ;
} while (pending);
对于tasklet的软中断处理函数执行过程解析如下:
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
static void tasklet_action(struct softirq_action *a)
struct tasklet_struct *list = __get_cpu_var(tasklet_vec).list //查找__tasklet_schedule(struct tasklet_struct *t)时加入的链表
-->遍历链表,执行每一个tasklet_struct结构中的func函数
为什么要使用工作队列work queue?(work queue和软中断的区别)
上面我们介绍的可延迟函数运行在中断上下文中(软中断的一个检查点就是do_IRQ退出的时候),于是导致了一些问题:软中断不能睡眠、不能阻塞。由于中断上下文出于内核态,没有进程切换,所以如果软中断一旦睡眠或者阻塞,将无法退出这种状态,导致内核会整个僵死。但可阻塞函数不能用在中断上下文中实现,必须要运行在进程上下文中,例如访问磁盘数据块的函数。因此,可阻塞函数不能用软中断来实现。但是它们往往又具有可延迟的特性。
因此在2.6版的内核中出现了在内核态运行的工作队列(替代了2.4内核中的任务队列)。它也具有一些可延迟函数的特点(需要被激活和延后执行),但是能够能够在不同的进程间切换,以完成不同的工作。
关于工作队列的实现机制,参考Linux工作队列实现机制 Linux内核:工作队列
Linux中断底半部机制的更多相关文章
- linux 中断底半部机制对比(任务队列,工作队列,软中断)--由linux RS485引出的血案【转】
转自:http://blog.chinaunix.net/uid-20768928-id-5077401.html 在LINUX RS485的使用过程中,由于各种原因,最后不得不使用中断底半部机制的方 ...
- linux底半部机制在视频采集驱动中的应用
最近在做一个arm+linux平台的视频驱动.本来这个驱动应该是做板子的第三方提供的,结果对方软件实力很差,自己做不了这个东西,外包给了一个暑期兼职的在读博士.学生嘛,只做过实验,没做过产品,给出的东 ...
- linux中断的下半部机制
一.中断处理为什么要下半部?Linux在中断处理中间中断处理分了上半部和下半部,目的就是提高系统的响应能力和并发能力.通俗一点来讲:当一个中断产生,调用该中断对应的处理程序(上半部)然后告诉系统,对应 ...
- linux中断编程
本文档只介绍中断编程所需的函数及应用,中断完整处理流程应参考文档<linux中断处理流程>,可参考文档<linux内核对中断的处理方式>对中断初步了解. 本文档基于3.14内核 ...
- Linux内核中断顶半部和底半部的理解
文章目录 中断上半部.下半部的概念 实现中断下半部的三种方法 软中断 软中断模版 tasklet tasklet函数模版 工作队列 工作队列函数模版 进程上下文和中断上下文 软中断和硬中断的区别 硬中 ...
- Linux设备驱动程序:中断处理之顶半部和底半部
http://blog.csdn.net/yuesichiu/article/details/8286469 设备的中断会打断内核中进程的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽可 ...
- linux中断处理-顶半部(top half)和底半部(bottom half) -转
原文:http://rensanshi.blog.163.com/blog/static/21395510820136282224877/ 设备的中断会打断内核中进程的正常调度和运行,系统对更高吞吐率 ...
- linux中断
[一].中断底半部 1. 软中断 --->>> 执行在中断上下文 --->>> 会被中断打断,不会被软中断或进程打断 --->>> ...
- 裸板中中断异常处理,linux中断异常处理 ,linux系统中断处理的API,中断处理函数的要求,内核中登记底半部的方式
1.linux系统中的中断处理 1.0裸板中中断异常是如何处理的? 以s5p6818+按键为例 1)按键中断的触发 中断源级配置 管脚功 ...
随机推荐
- Codeforces Round #541 (Div. 2) A.Sea Battle
链接:https://codeforces.com/contest/1131/problem/A 题意: 给两个矩形,一个再上一个在下,求两个矩形合并的周围一圈的面积. 思路: 因为存在下面矩形宽度大 ...
- 18000 Two String 暴力。——— 读题
http://acm.scau.edu.cn:8000/uoj/mainMenu.html 18000 Two String 时间限制:1000MS 内存限制:65535K提交次数:0 通过次数:0 ...
- 海德汉S3078长度计
手头有一款海德汉S3078的增量式长度计. 图三为长度计引出线缆的针脚编号,其中,Up和传感器Up短路,0V与传感器0V短路,其他为输出信号线缆.时序如图: Ua1和Ua2两个信号的相位,可以得出长度 ...
- js js弹出框、对话框、提示框、弹窗总结
js弹出框.对话框.提示框.弹窗总结 一.JS的三种最常见的对话框 //====================== JS最常用三种弹出对话框 ======================== //弹 ...
- BZOJ1485: [HNOI2009]有趣的数列(Catalan数,质因数分解求组合数)
题意 挺简洁的. 我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件: (1)它是从1到2n共2n个整数的一个排列{ai}: (2)所有的奇数项满足a1<a3<…<a ...
- POSTGRESQL 存储过程实战
转了N多的SQL语句,可是自己用时,却到处是坑啊,啊,啊!!!!!!!!!!!!!!! 想写一个获取表中最新ID值. 上代码 CREATE TABLE department( ID INT PRIMA ...
- 解析C++普通局部变量与指针类型的对象变量的应用区别
首先我们先来了解一下MFC中的CPtrArray类,他可以说是CObject类型指针对象的集合.通过intAdd( CObject* newElement );注意参数是一个指针类型)可以向集合中添加 ...
- uvm_pkg——老板,打包带走
Thus spake the master programmer: “After three day without programming, life becomes meaningless.” 编 ...
- pingall脚本
p i n g a l l:一个按照/ e t c / h o s t s文件中的条目逐一p i n g所有主机的脚本 它能够按照/ e t c / h o s t s文件中的条目逐一p i n g所 ...
- 基于phpExcel写的excel类(导出为Excel)
<?php /* * 类的功能 * 传入二位数组导出excel * 传入excel 导出二位数组 * @author mrwu */ require('PHPExcel.php'); requi ...