來自:http://blog.csdn.net/zhangskd/article/details/21992933java
記錄一個軟中斷問題:http://huoding.com/2013/10/30/296
概述
從本質上來說,中斷是一種電信號,當設備有某種事件發生時,它就會產生中斷,經過總線把電信號發送給中斷控制器。linux
若是中斷的線是激活的,中斷控制器就把電信號發送給處理器的某個特定引腳。處理器因而當即中止本身正在作的事,數組
跳到中斷處理程序的入口點,進行中斷處理。網絡
![](http://static.javashuo.com/static/loading.gif)
(1) 硬中斷ide
由與系統相連的外設(好比網卡、硬盤)自動產生的。主要是用來通知操做系統系統外設狀態的變化。好比當網卡收到數據包函數
的時候,就會發出一箇中斷。咱們一般所說的中斷指的是硬中斷(hardirq)。ui
(2) 軟中斷this
爲了知足實時系統的要求,中斷處理應該是越快越好。linux爲了實現這個特色,當中斷髮生的時候,硬中斷處理那些短期spa
就能夠完成的工做,而將那些處理事件比較長的工做,放到中斷以後來完成,也就是軟中斷(softirq)來完成。操作系統
(3) 中斷嵌套
Linux下硬中斷是能夠嵌套的,可是沒有優先級的概念,也就是說任何一個新的中斷均可以打斷正在執行的中斷,但同種中斷
除外。軟中斷不能嵌套,但相同類型的軟中斷能夠在不一樣CPU上並行執行。
(4) 軟中斷指令
int是軟中斷指令。
中斷向量表是中斷號和中斷處理函數地址的對應表。
int n - 觸發軟中斷n。相應的中斷處理函數的地址爲:中斷向量表地址 + 4 * n。
(5)硬中斷和軟中斷的區別
軟中斷是執行中斷指令產生的,而硬中斷是由外設引起的。
硬中斷的中斷號是由中斷控制器提供的,軟中斷的中斷號由指令直接指出,無需使用中斷控制器。
硬中斷是可屏蔽的,軟中斷不可屏蔽。
硬中斷處理程序要確保它能快速地完成任務,這樣程序執行時纔不會等待較長時間,稱爲上半部。
軟中斷處理硬中斷未完成的工做,是一種推後執行的機制,屬於下半部。
開關
(1) 硬中斷的開關
簡單禁止和激活當前處理器上的本地中斷:
local_irq_disable();
local_irq_enable();
保存本地中斷系統狀態下的禁止和激活:
unsigned long flags;
local_irq_save(flags);
local_irq_restore(flags);
(2) 軟中斷的開關
禁止下半部,如softirq、tasklet和workqueue等:
local_bh_disable();
local_bh_enable();
須要注意的是,禁止下半部時仍然能夠被硬中斷搶佔。
(3) 判斷中斷狀態
#define in_interrupt() (irq_count()) // 是否處於中斷狀態(硬中斷或軟中斷)
#define in_irq() (hardirq_count()) // 是否處於硬中斷
#define in_softirq() (softirq_count()) // 是否處於軟中斷
硬中斷
(1) 註冊中斷處理函數
註冊中斷處理函數:
-
- int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
- const char *name, void *dev);
中斷處理函數自己:
- typedef irqreturn_t (*irq_handler_t) (int, void *);
-
- enum irqreturn {
- IRQ_NONE,
- IRQ_HANDLED,
- IRQ_WAKE_THREAD,
- };
- typedef enum irqreturn irqreturn_t;
- #define IRQ_RETVAL(x) ((x) != IRQ_NONE)
(2) 註銷中斷處理函數
-
- void free_irq(unsigned int irq, void *dev_id);
軟中斷
(1) 定義
軟中斷是一組靜態定義的下半部接口,能夠在全部處理器上同時執行,即便兩個類型相同也能夠。
但一個軟中斷不會搶佔另外一個軟中斷,惟一能夠搶佔軟中斷的是硬中斷。
軟中斷由softirq_action結構體表示:
- struct softirq_action {
- void (*action) (struct softirq_action *);
- };
目前已註冊的軟中斷有10種,定義爲一個全局數組:
- static struct softirq_action softirq_vec[NR_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,
- NR_SOFTIRQS
- };
(2) 註冊軟中斷處理函數
-
- void open_softirq(int nr, void (*action) (struct softirq_action *))
- {
- softirq_vec[nr].action = action;
- }
例如:
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
(3) 觸發軟中斷
調用raise_softirq()來觸發軟中斷。
- void raise_softirq(unsigned int nr)
- {
- unsigned long flags;
- local_irq_save(flags);
- raise_softirq_irqoff(nr);
- local_irq_restore(flags);
- }
-
- inline void rasie_softirq_irqsoff(unsigned int nr)
- {
- __raise_softirq_irqoff(nr);
-
-
- if (! in_interrupt())
- wakeup_softirqd(void);
- }
Percpu變量irq_cpustat_t中的__softirq_pending是等待處理的軟中斷的位圖,經過設置此變量
便可告訴內核該執行哪些軟中斷。
- static inline void __rasie_softirq_irqoff(unsigned int nr)
- {
- trace_softirq_raise(nr);
- or_softirq_pending(1UL << nr);
- }
-
- typedef struct {
- unsigned int __softirq_pending;
- unsigned int __nmi_count;
- } irq_cpustat_t;
-
- irq_cpustat_t irq_stat[];
- #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
- #define or_softirq_pending(x) percpu_or(irq_stat.__softirq_pending, (x))
- #define local_softirq_pending() percpu_read(irq_stat.__softirq_pending)
喚醒ksoftirqd內核線程處理軟中斷。
- static void wakeup_softirqd(void)
- {
-
- struct task_struct *tsk = __get_cpu_var(ksoftirqd);
-
- if (tsk && tsk->state != TASK_RUNNING)
- wake_up_process(tsk);
- }
在下列地方,待處理的軟中斷會被檢查和執行:
1. 從一個硬件中斷代碼處返回時
2. 在ksoftirqd內核線程中
3. 在那些顯示檢查和執行待處理的軟中斷的代碼中,如網絡子系統中
而不論是用什麼方法喚起,軟中斷都要在do_softirq()中執行。若是有待處理的軟中斷,
do_softirq()會循環遍歷每個,調用它們的相應的處理程序。
在中斷處理程序中觸發軟中斷是最多見的形式。中斷處理程序執行硬件設備的相關操做,
而後觸發相應的軟中斷,最後退出。內核在執行完中斷處理程序之後,立刻就會調用
do_softirq(),因而軟中斷開始執行中斷處理程序完成剩餘的任務。
下面來看下do_softirq()的具體實現。
- asmlinkage void do_softirq(void)
- {
- __u32 pending;
- unsigned long flags;
-
-
- if (in_interrupt())
- return;
-
- local_irq_save(flags);
- pending = local_softirq_pending();
- if (pending)
- __do_softirq();
- local_irq_restore(flags);
- }
- asmlinkage void __do_softirq(void)
- {
- struct softirq_action *h;
- __u32 pending;
-
- int max_restart = MAX_SOFTIRQ_RESTART;
- int cpu;
-
- pending = local_softirq_pending();
- account_system_vtime(current);
-
- __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);
- lockdep_softirq_enter();
- cpu = smp_processor_id();
-
- restart:
-
- set_softirq_pending(0);
- local_irq_enable();
- h = softirq_vec;
- do {
- if (pending & 1) {
- unsigned int vec_nr = h - softirq_vec;
- int prev_count = preempt_count();
- kstat_incr_softirqs_this_cpu(vec_nr);
-
- trace_softirq_entry(vec_nr);
- h->action(h);
- trace_softirq_exit(vec_nr);
-
- if (unlikely(prev_count != preempt_count())) {
- printk(KERN_ERR "huh, entered softirq %u %s %p" "with preempt_count %08x,"
- "exited with %08x?\n", vec_nr, softirq_to_name[vec_nr], h->action, prev_count,
- preempt_count());
- }
- rcu_bh_qs(cpu);
- }
- h++;
- pending >>= 1;
- } while(pending);
-
- 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(SOFTIRQ_OFFSET);
- }
(4) ksoftirqd內核線程
內核不會當即處理從新觸發的軟中斷。
當大量軟中斷出現的時候,內核會喚醒一組內核線程來處理。
這些線程的優先級最低(nice值爲19),這能避免它們跟其它重要的任務搶奪資源。
但它們最終確定會被執行,因此這個折中的方案可以保證在軟中斷不少時用戶程序不會
由於得不處處理時間而處於飢餓狀態,同時也保證過量的軟中斷最終會獲得處理。
每一個處理器都有一個這樣的線程,名字爲ksoftirqd/n,n爲處理器的編號。
- static int run_ksoftirqd(void *__bind_cpu)
- {
- set_current_state(TASK_INTERRUPTIBLE);
- current->flags |= PF_KSOFTIRQD;
-
- while(! kthread_should_stop()) {
- preempt_disable();
-
- if (! local_softirq_pending()) {
- preempt_enable_no_resched();
- schedule();
- preempt_disable():
- }
-
- __set_current_state(TASK_RUNNING);
-
- while(local_softirq_pending()) {
-
- if (cpu_is_offline(long)__bind_cpu))
- goto wait_to_die;
-
- do_softirq();
-
- preempt_enable_no_resched();
- cond_resched();
- preempt_disable();
- rcu_note_context_switch((long)__bind_cpu);
- }
-
- preempt_enable();
- set_current_state(TASK_INTERRUPTIBLE);
- }
-
- __set_current_state(TASK_RUNNING);
- return 0;
-
- wait_to_die:
- preempt_enable();
-
- set_current_state(TASK_INTERRUPTIBLE);
- while(! kthread_should_stop()) {
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
-
- __set_current_state(TASK_RUNNING);
- return 0;