📄 中断.txt
字号:
中断
中断
目 录
中断
软中断
硬中断
定时器代码分析
from aka
硬件中断
软中断
from lisolog
index
内部中断
外部中断
后续处理
软中断代码线索
2. 4软中断机制
中断
Linux系统中有很多不同的硬件设备。你可以同步使用这些设备,也就是说你可以发出一个请求,然后等待一直到设备完成操作以后再进行其他的工作。但这种方法的效率却非常的低,因为操作系统要花费很多的等待时间。一个更为有效的方法是发出请求以后,操作系统继续其他的工作,等设备完成操作以后,给操作系统发送一个中断,操作系统再继续处理和此设备有关的操作。
在将多个设备的中断信号送往CPU的中断插脚之前,系统经常使用中断控制器来综合多个设备的中断。这样即可以节约CPU的中断插脚,也可以提高系统设计的灵活性。中断控制器用来控制系统的中断,它包括屏蔽和状态寄存器。设置屏蔽寄存器的各个位可以允许或屏蔽某一个中断,状态寄存器则用来返回系统中正在使用的中断。
大多数处理器处理中断的过程都相同。当一个设备发出中段请求时,CPU停止正在执行的指令,转而跳到包括中断处理代码或者包括指向中断处理代码的转移指令所在的内存区域。这些代码一般在CPU的中断方式下运行。在此方式下,将不会再有中断发生。但有些CPU的中断有自己的优先权,这样,更高优先权的中断则可以发生。这意味着第一级的中断处理程序必须拥有自己的堆栈,以便在处理更高级别的中断前保存CPU的执行状态。当中断处理完毕以后,CPU将恢复到以前的状态,继续执行中断处理前正在执行的指令。
中断处理程序十分简单有效,这样,操作系统就不会花太长的时间屏蔽其他的中断。
[设置Softirq]
cpu_raise_softirq是一个轮训,唤醒ksoftirqd_CPU0内核线程, 进行管理
cpu_raise_softirq
|__cpu_raise_softirq
|wakeup_softirqd
|wake_up_process
·cpu_raise_softirq [kernel/softirq.c]
·__cpu_raise_softirq [include/linux/interrupt.h]
·wakeup_softirq [kernel/softirq.c]
·wake_up_process [kernel/sched.c]
[执行Softirq]
当内核线程ksoftirqd_CPU0被唤醒, 它会执行队列里的工作。当然ksoftirqd_CPU0也是一个死循环:
for (;;) {
if (!softirq_pending(cpu))
schedule();
__set_current_state(TASK_RUNNING);
while (softirq_pending(cpu)) {
do_softirq();
if (current->need_resched)
schedule
}
__set_current_state(TASK_INTERRUPTIBLE)
}
·ksoftirqd [kernel/softirq.c]
[目录]
软中断
发信人: fist (星仔迷), 信区: SysInternals WWW-POST
标 题: 软中断
发信站: 武汉白云黄鹤站 (Thu Mar 22 14:12:46 2001) , 转信
软中断「一」
一、 引言
软中断是linux系统原“底半处理”的升级,在原有的基础上发展的新的处理方式,以适应多cpu
、多线程的软中断处理。要了解软中断,我们必须要先了原来底半处理的处理机制。
二、底半处理机制(基于2.0.3版本)
某些特殊时刻我们并不愿意在核心中执行一些操作。例如中断处理过程中。当中断发生时处理器将停止当前的工作,
操作系统将中断发送到相应的设备驱动上去。由于此时系统中其他程序都不能运行,
所以设备驱动中的中断处理过程不宜过长。有些任务最好稍后执行。Linux底层部分处理机制可以让设备驱动和Linux核心其他部分将这些工作进行排序以延迟执行。
系统中最多可以有32个不同的底层处理过程;bh_base是指向这些过程入口的指针数组。而bh_active和
bh_mask用来表示那些处理过程已经安装以及那些处于活动状态。如果bh_mask的第N位置位则表示bh_base的
第N个元素包含底层部分处理例程。如果bh_active的第N位置位则表示第N个底层处理过程例程可在调度器认
为合适的时刻调用。这些索引被定义成静态的;定时器底层部分处理例程具有最高优先级(索引值为0),
控制台底层部分处理例程其次(索引值为1)。典型的底层部分处理例程包含与之相连的任务链表。例如
immediate底层部分处理例程通过那些需要被立刻执行的任务的立即任务队列(tq_immediate)来执行。
--引自David A Rusling的《linux核心》。
三、对2.4.1 软中断处理机制
下面,我们进入软中断处理部份(softirq.c):
由softirq.c的代码阅读中,我们可以知道,在系统的初始化过程中(softirq_init()),它使用了两个数组:bh_task_vec[32],softirq_vec[32]。其中,bh_task_vec[32]填入了32个bh_action()的入口地址,但soft_vec[32]中,只有softirq_vec[0],和softirq_vec[3]分别填入了tasklet_action()和tasklet_hi_action()的地址。其余的保留它用。
当发生软中断时,系统并不急于处理,只是将相应的cpu的中断状态结构中的active
的相应的位置位,并将相应的处理函数挂到相应的队列,然后等待调度时机来临(如:schedule(),
系统调用返回异常时,硬中断处理结束时等),系统调用do_softirq()来测试active位,再调用被激活的进程在这处过程中,软中断的处理与底半处理有了差别,active
和mask不再对应bh_base[nr],
而是对应softirq_vec[32]。在softirq.c中,我们只涉及了softirq_vec[0]、softirq_vec[3]。这两者分别调用了tasklet_action()和tasklet_hi_action()来进行后续处理。这两个过程比较相似,大致如下:
1 锁cpu的tasklet_vec[cpu]链表,取出链表,将原链表清空,解锁,还给系统。
2 对链表进行逐个处理。
3 有无法处理的,(task_trylock(t)失败,可能有别的进程锁定),插回系统链表。至此,系统完成了一次软中断的处理。
接下来有两个问题:
1 bh_base[]依然存在,但应在何处调用?
2 tasklet_vec[cpu]队列是何时挂上的?
四、再探讨
再次考查softirq.c 的bh_action()部份,发现有两个判断:
A:if(!spin_trylock(&global_bh_lock))goto:rescue 指明如果global_bh_lock
不能被锁上(已被其它进程锁上),则转而执行rescue,将bh_base[nr]挂至tasklet_hi_vec[cpu]队列中。等候中断调度。
B:if(!hardirq_trylock(cpu)) goto tescue unlock 此时有硬中断发生,放入队列推迟执行。若为空闲,现在执行。
由此可见,这部分正是对应底半处理的程序,bh_base[]的延时处理正是底半处理的特点,可以推测,如果没有其它函数往tasklet_hi_vec[cpu]队列挂入,那tasklet_hi_vec[cpu]正完全对应着bh_base[]底半处理
在bh_action()中,把bh_ation()挂入tasklet_hi_vec[cpu]的正是mark_bh(),在整个源码树中查找,发现调用mark_bh()的函数很多,可以理解,软中断产生之时,相关的函数会调用mark_bh(),将bh_action挂上tasklet_hi_vec队列,而bh_action()的作用不过是在发现bh_base[nr]暂时无法处理时重返队列的方法。
由此可推测tasklet_vec队列的挂接应与此相似,查看interrupt.h,找到tasklet_schedule()函数:
157 static inline void tasklet_schedule(struct tasklet_struct *t)
158 {
159 if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
160 int cpu = smp_processor_id();
161 unsigned long flags;
162
163 local_irq_save(flags);
164 t->next = tasklet_vec[cpu].list;
165 tasklet_vec[cpu].list = t; /*插入队列。
166 __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
167 local_irq_restore(flags);
168 }
169 }
正是它为tasklet_vec[cpu]队列的建立立下了汗马功劳,在源码树中,它亦被多个模块调用,来完成它的使命。
至此,我们可以描绘一幅完整的软中断处理图了。
现在,再来考查do_softirq()的softirq_vec[32],在interrupt.h中有如下定义:
56 enum
57 {
58 HI_SOFTIRQ=0,
59 NET_TX_SOFTIRQ,
60 NET_RX_SOFTIRQ,
61 TASKLET_SOFTIRQ
62 };
这四个变量应都是为softirq_vec[]的下标,那么,do_softirq()也将会处理NET_TX_SOFTIRQ和NET_RX_SOFTIRQ,是否还处理其它中断,这有待探讨。也许,这个do_softirq()有着极大的拓展性,等着我们去开发呢。
主要通过__cpu_raise_softirq来设置
在hi_tasklet(也就是一般用于bh的)的处理里面,在处理完当前的队列后,会将补充的队列重新挂上,然后标记(不管是否补充队列里面有tasklet):
local_irq_disable();
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
__cpu_raise_softirq(cpu, HI_SOFTIRQ);
local_irq_enable();
因此,对mark_bh根本不用设置这个active位。对于一般的tasklet也一样:
local_irq_disable();
t->next = tasklet_vec[cpu].list;
tasklet_vec[cpu].list = t;
__cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
local_irq_enable();
其它的设置,可以检索上面的__cpu_raise_softirq
bottom half, softirq, tasklet, tqueue
[bottom half]
bh_base[32]
|
\/
bh_action();
|
\/
bh_task_vec[32];
| mark_bh(), tasklet_hi_schedule()
\/
task_hi_action
bh_base对应的是32个函数,这些函数在bh_action()中调用
static void bh_action(unsigned long nr)
{
int cpu = smp_processor_id();
if (!spin_trylock(&global_bh_lock))
goto resched;
if (!hardirq_trylock(cpu))
goto resched_unlock;
if (bh_base[nr])
bh_base[nr]();
hardirq_endlock(cpu);
spin_unlock(&global_bh_lock);
return;
resched_unlock:
spin_unlock(&global_bh_lock);
resched:
mark_bh(nr);
}
在软中断初始化时,将bh_action()放到bh_task_vec[32]中,bh_task_vec[32]中元素的类型是tasklet_struct,系统使用mark_bh()或task_hi_schedule()函数将它挂到task_hi_vec[]的对列中,在系统调用do_softirq()时执行。
static inline void mark_bh(int nr)
{
tasklet_hi_schedule(bh_task_vec+nr);
}
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
int cpu = smp_processor_id();
unsigned long flags;
local_irq_save(flags);
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
__cpu_raise_softirq(cpu, HI_SOFTIRQ);
local_irq_restore(flags);
}
}
[softirq]
softirq_vec[32];
struct softirq_action
{
void (*action)(struct softirq_action *);
void *data;
};
软中断对应一个softirq_action的结构,在do_softirq()中调用相应的action()做处理。
软中断初始化时只设置了0,3两项,对应的action是task_hi_action和task_action.
1: task_hi_action
/\
|
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -