?? 中斷.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 + -