【版权声明:尊重原创。转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】

        中断处理程序以异步方式执行,其会打断其它重要代码,其执行时该中断同级的其它中断会被屏蔽,而且当前处理器上全部其它中断都有可能会被屏蔽掉,还有中断处理程序不能堵塞,所以中断处理须要尽快结束。因为中断处理程序的这些缺陷,导致了中断处理程序仅仅是整个硬件中断处理流程的一部分,对于那些对时间要求不高的任务。留给中断处理流程的另外一部分,也就是本节要讲的中断处理流程的下半部。
        那哪些工作由中断处理程序完毕。哪些工作留给下半部来运行呢?事实上上半部和下半部的工作划分不存在某种严格限制,这主要取决于驱动程序开发人员自己的推断,一般最好能将中断处理程序运行时间缩短到最小。中断处理程序差点儿都须要通过操作硬件对中断的到达进行确认,有时还会做对时间很敏感的工作(如拷贝数据),其余的工作基本上留给下半部来处理,下半部就是运行与中断处理密切相关但中断处理程序本身不运行的工作。

一般对时间很敏感、和硬件相关、要保证不被其他中断(特别是同样的中断)打断的这些任务放在中断处理程序中运行。其他任务考虑放在下半部运行。

        那下半部什么时候运行呢?下半部不须要指定明白运行时间,仅仅要把任务推迟一点,让它们在系统不太忙且中断恢复后运行就能够了,并且运行期间能够对应全部中断。
        上半部仅仅能通过中断处理程序实现,而下半部能够有多种机制来实现,在2.6.32版本号中。有三种不同形式的下半部实现机制:软中断、tasklet、工作队列。以下来看一下这三种下半部的实现。

软中断
在start_kernerl()函数中,系统初始化软中断。

asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[]; smp_setup_processor_id();
......
softirq_init();//初始化软中断
...... /* Do the rest non-__init'ed, we're now alive */
rest_init();
}

在softirq_init()中会注冊两个经常使用类型的软中断, 详细代码例如以下(位于kernel/softirq.c):

void __init softirq_init(void)
{
int cpu; for_each_possible_cpu(cpu) {
int i; per_cpu(tasklet_vec, cpu).tail =
&per_cpu(tasklet_vec, cpu).head;
per_cpu(tasklet_hi_vec, cpu).tail =
&per_cpu(tasklet_hi_vec, cpu).head;
for (i = 0; i < NR_SOFTIRQS; i++)
INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
} register_hotcpu_notifier(&remote_softirq_cpu_notifier); //此处注冊两个软中断
open_softirq(TASKLET_SOFTIRQ, tasklet_action); open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
注冊函数open_softirq()參数含义:

nr:软中断类型 action:软中断处理函数
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
softirq_action结构表示软中断,定义在<include/linux/interrupt.h>

struct softirq_action
{
void (*action)(struct softirq_action *);
}
文件<kernel/softirq.c>中定义了32个该结构体的数组:

static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
每注冊一个软中断都会占该数组一个位置。因此系统中最多有32个软中断。

从上面的代码中,我们能够看到:open_softirq()中.事实上就是对softirq_vec数组的nr项赋值.softirq_vec是一个32元素的数组,实际上linux内核仅仅使用了几项:

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
frequency threaded job scheduling. For almost all the purposes
tasklets are more than enough. F.e. all serial device BHs et
al. should be converted to tasklets, not to softirqs.
*/ enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS
};
          那么软中断注冊完毕之后,什么时候触发软中断处理函数运行呢?通常情况下,软中断会在中断处理程序返回前标记它,使其在稍后合适的时候被运行。在下列地方。待处理的软中断会被检查和运行:

1.处理完一个硬件中断以后;

2.在ksoftirqd内核线程中;

3.在那些显示检查和运行待处理的软中断的代码中,如网络子系统中。

有关网络子系统内容可參考文章:http://blog.csdn.net/shallnet/article/details/26269781

不管怎样。软中断会在do_softirq()(位于<kernel/softirq.c>中)中运行,假设有待处理的软中断,do_softirq会循环遍历每个,调用他们的软中断处理程序。

asmlinkage void do_softirq(void) {     __u32 pending;     unsigned long flags;     //假设在硬件中断环境中就退出,软中断不能够在硬件中断上下文或者是在软中断环境中使用,使用in_interrupt()来防止软中断嵌套。和抢占硬中断环境。

if (in_interrupt())         return;     //禁止本地中断     local_irq_save(flags);     pending = local_softirq_pending();

    //假设有软中断要处理,则进入__do_softirq()
if (pending) __do_softirq(); local_irq_restore(flags);
以下看一下__do_softirq()的实现:

asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu; pending = local_softirq_pending(); //pending用于保留待处理软中断32位位图
account_system_vtime(current); __local_bh_disable((unsigned long)__builtin_return_address(0));
lockdep_softirq_enter(); cpu = smp_processor_id();
restart:
/* Reset the pending bitmask before enabling irqs */
set_softirq_pending(0); local_irq_enable(); h = softirq_vec; do {
if (pending & 1) { //假设pending第n位被设置为1,那么处理第n位相应类型的软中断
int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(h - softirq_vec); trace_softirq_entry(h, softirq_vec);
h->action(h); //运行软中断处理函数
trace_softirq_exit(h, softirq_vec);
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %td %s %p"
"with preempt_count %08x,"
" exited with %08x?\n", h - softirq_vec,
softirq_to_name[h - softirq_vec],
h->action, prev_count, preempt_count());
preempt_count() = prev_count;
} rcu_bh_qs(cpu);
}
h++;
pending >>= 1; //pending右移一位,循环检查其每一位
} while (pending); //直到pending变为0,pending最多32位,所以循环最多运行32次。 local_irq_disable(); pending = local_softirq_pending();
if (pending && --max_restart)
goto restart; if (pending)
wakeup_softirqd(); lockdep_softirq_exit(); account_system_vtime(current);
_local_bh_enable();
}
        使用软中断必需要在编译期间静态注冊,一般仅仅有像网络这样对性能要求高的情况才使用软中断,文章前面我们也看到,系统中注冊的软中断就那么几个。大部分时候,使用下半部第二种机制tasklet的情况很多其它一些。tasklet能够动态的注冊。能够被看作是一种性能和易用性之间寻求平衡的一种产物。其实。大部分驱动程序都是用tasklet来实现他们的下半部。

把握linux内核设计思想(三):下半部机制之软中断的更多相关文章

  1. 把握linux内核设计思想系列【转】

    转自:http://blog.csdn.net/shallnet/article/details/47734053 版权声明:本文为博主原创文章,未经博主允许不得转载.如果您觉得文章对您有用,请点击文 ...

  2. 把握linux内核设计思想系列

    [版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 本专栏分析linux内核的设计实现,包含系统调用.中断.下半部机制.时间管理. ...

  3. 把握linux内核设计思想(五):下半部机制之工作队列及几种机制的选择

    [版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途]         工作队列是下半部的第二种将工作推后运行形式.和软中断.task ...

  4. 把握linux内核设计思想(七):内核定时器和定时运行

    [版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途]         前面章节说到了把工作推后到除如今以外的时间运行的机制是下半部机 ...

  5. 把握linux内核设计思想(十三):内存管理之进程地址空间

    [版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet.文章仅供学习交流,请勿用于商业用途] 进程地址空间由进程可寻址的虚拟内存组成,Linux 的虚拟地址空间为0~4G字 ...

  6. 把握linux内核设计思想(二):硬中断及中断处理

    [版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 操作系统负责管理硬件设备.为了使系统和硬件设备的协同工作不减少机器性能.系统和 ...

  7. 把握linux内核设计思想(十二):内存管理之slab分配器

    [版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流.请勿用于商业用途] 上一节最后说到对于小内存区的请求,假设採用伙伴系统来进行分配,则会在页内产生非 ...

  8. Linux内核设计第三周——构造一个简单的Linux系统

    Linux内核设计第三周 ——构造一个简单的Linux系统 一.知识点总结 计算机三个法宝: 存储程序计算机 函数调用堆栈 中断 操作系统两把宝剑: 中断上下文的切换 进程上下文的切换 linux内核 ...

  9. Linux内核设计第三周学习总结 跟踪分析Linux内核的启动过程

    陈巧然 原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验步骤 登陆实验楼虚 ...

随机推荐

  1. Matlab--从入门到精通(chapter2 matlab 基础知识)

    Chapter2 Matlab 基础知识 1.基本数学运算符号 注:矩阵的右除是一般意义的除法,但是左除具有对称意义,即A./B=B.\A 2. 命令行中的常用标点 3.常见的操作命令 4.输出数据显 ...

  2. 安装lnmp前请先运行screen

    当通过putty或者SecureCRT安装lnmp时, 网络突然掉线或者不小心putty被关掉等等原因, 造成lnmp安装过程被中断怎么办? 其实防止这种现象很简单, 只要在安装lnmp前执行scre ...

  3. (WC2018模拟十二)【FJOI2016集训Day7T2】点对游戏

    题解: 还好...看懂题目就好做了.(Orzdyh) 首先选择的点是等概率随机的,也就是说每种选择结果的概率都是一样的,所以选择一个点的时候已经选择的点不会有影响,那么就可以直接算出点对个数再求总体的 ...

  4. CSS常用样式--font

    CSS font 属性 参考:W3school- CSS font 所有浏览器都支持 font 属性,可在一个声明中设置所有字体属性,各属性需按顺序,语法如下: selector{ font:styl ...

  5. 重载和const形参

    1.int lookup(string p); 2.int lookup(const string p);//同1 3.int lookup(string *);//传入一个指针,指针指向string ...

  6. LVM的创建与挂载

    LVM的诞生: 由于传统的磁盘管理不能对磁盘进行磁盘管理,比如我把/dev/sdb1挂载到了/liu目录下,但是因为数据量过大的原因,此文件系统磁盘利用率已经高达98%,那么我可以直接对这个磁盘进行扩 ...

  7. 【codeforces 196B】Infinite Maze

    [题目链接]:http://codeforces.com/problemset/problem/196/B [题意] 给你一个n*m的棋盘; 然后你能够无限复制这个棋盘; 在这个棋盘上你有一个起点s; ...

  8. 在android中,编译的项目使用到第三方jar的导入方法 终极版!

    1,在android系统环境中编译自己的项目时,往往会用到第三方jar包.这些jar包在eclipse中加入编译,一路畅通,由于eclipse已经帮助你配置好了.可是当把这个项目复制到系统环境中编译时 ...

  9. iOS:UISplitViewController的创建

    UISplitViewController是iPad特有的系统方法,主要效果就是呈现iPad的经典切割界面 代码创建实例: - (BOOL)application:(UIApplication *)a ...

  10. YII显示sql进行调试

    进行插入測试: 一个简单控制器: function actionJia(){ $goods_model = new Goods(); //进行加入有别于查询不能使用以下这样的方式 // $goods_ ...