[Linux内核]软中断与硬中断
轉自:http://blog.csdn.net/zhangskd/article/details/21992933
本文主要內容:硬中斷 / 軟中斷的原理和實現
內核版本:2.6.37
Author:zhangskd @ csdn blog
?
概述
?
從本質上來講,中斷是一種電信號,當設備有某種事件發生時,它就會產生中斷,通過總線把電信號發送給中斷控制器。
如果中斷的線是激活的,中斷控制器就把電信號發送給處理器的某個特定引腳。處理器于是立即停止自己正在做的事,
跳到中斷處理程序的入口點,進行中斷處理。
?
(1) 硬中斷
由與系統相連的外設(比如網卡、硬盤)自動產生的。主要是用來通知操作系統系統外設狀態的變化。比如當網卡收到數據包
的時候,就會發出一個中斷。我們通常所說的中斷指的是硬中斷(hardirq)。
?
(2) 軟中斷
為了滿足實時系統的要求,中斷處理應該是越快越好。linux為了實現這個特點,當中斷發生的時候,硬中斷處理那些短時間
就可以完成的工作,而將那些處理事件比較長的工作,放到中斷之后來完成,也就是軟中斷(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) 注冊中斷處理函數
注冊中斷處理函數:
1 /** 2 * irq: 要分配的中斷號 3 * handler: 要注冊的中斷處理函數 4 * flags: 標志(一般為0) 5 * name: 設備名(dev->name) 6 * dev: 設備(struct net_device *dev),作為中斷處理函數的參數 7 * 成功返回0 8 */ 9 10 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, 11 const char *name, void *dev);
?
中斷處理函數本身:
1 typedef irqreturn_t (*irq_handler_t) (int, void *); 2 3 /** 4 * enum irqreturn 5 * @IRQ_NONE: interrupt was not from this device 6 * @IRQ_HANDLED: interrupt was handled by this device 7 * @IRQ_WAKE_THREAD: handler requests to wake the handler thread 8 */ 9 enum irqreturn { 10 IRQ_NONE, 11 IRQ_HANDLED, 12 IRQ_WAKE_THREAD, 13 }; 14 typedef enum irqreturn irqreturn_t; 15 #define IRQ_RETVAL(x) ((x) != IRQ_NONE)
(2) 注銷中斷處理函數
1 /** 2 * free_irq - free an interrupt allocated with request_irq 3 * @irq: Interrupt line to free 4 * @dev_id: Device identity to free 5 * 6 * Remove an interrupt handler. The handler is removed and if the 7 * interrupt line is no longer in use by any driver it is disabled. 8 * On a shared IRQ the caller must ensure the interrupt is disabled 9 * on the card it drives before calling this function. The function does 10 * not return until any executing interrupts for this IRQ have completed. 11 * This function must not be called from interrupt context. 12 */ 13 14 void free_irq(unsigned int irq, void *dev_id);
?
軟中斷
?
(1) 定義
軟中斷是一組靜態定義的下半部接口,可以在所有處理器上同時執行,即使兩個類型相同也可以。
但一個軟中斷不會搶占另一個軟中斷,唯一可以搶占軟中斷的是硬中斷。?
軟中斷由softirq_action結構體表示:
1 struct softirq_action { 2 void (*action) (struct softirq_action *); /* 軟中斷的處理函數 */ 3 }; ?
目前已注冊的軟中斷有10種,定義為一個全局數組:
1 static struct softirq_action softirq_vec[NR_SOFTIRQS]; 2 3 enum { 4 HI_SOFTIRQ = 0, /* 優先級高的tasklets */ 5 TIMER_SOFTIRQ, /* 定時器的下半部 */ 6 NET_TX_SOFTIRQ, /* 發送網絡數據包 */ 7 NET_RX_SOFTIRQ, /* 接收網絡數據包 */ 8 BLOCK_SOFTIRQ, /* BLOCK裝置 */ 9 BLOCK_IOPOLL_SOFTIRQ, 10 TASKLET_SOFTIRQ, /* 正常優先級的tasklets */ 11 SCHED_SOFTIRQ, /* 調度程序 */ 12 HRTIMER_SOFTIRQ, /* 高分辨率定時器 */ 13 RCU_SOFTIRQ, /* RCU鎖定 */ 14 NR_SOFTIRQS /* 10 */ 15 };
?
(2) 注冊軟中斷處理函數
1 /** 2 * @nr: 軟中斷的索引號 3 * @action: 軟中斷的處理函數 4 */ 5 6 void open_softirq(int nr, void (*action) (struct softirq_action *)) 7 { 8 softirq_vec[nr].action = action; 9 } ?
例如:
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
?
(3) 觸發軟中斷?
調用raise_softirq()來觸發軟中斷。
1 void raise_softirq(unsigned int nr) 2 { 3 unsigned long flags; 4 local_irq_save(flags); 5 raise_softirq_irqoff(nr); 6 local_irq_restore(flags); 7 } 8 9 /* This function must run with irqs disabled */ 10 inline void rasie_softirq_irqsoff(unsigned int nr) 11 { 12 __raise_softirq_irqoff(nr); 13 14 /* If we're in an interrupt or softirq, we're done 15 * (this also catches softirq-disabled code). We will 16 * actually run the softirq once we return from the irq 17 * or softirq. 18 * Otherwise we wake up ksoftirqd to make sure we 19 * schedule the softirq soon. 20 */ 21 if (! in_interrupt()) /* 如果不處于硬中斷或軟中斷 */ 22 wakeup_softirqd(void); /* 喚醒ksoftirqd/n進程 */ 23 }
Percpu變量irq_cpustat_t中的__softirq_pending是等待處理的軟中斷的位圖,通過設置此變量
即可告訴內核該執行哪些軟中斷。
1 static inline void __rasie_softirq_irqoff(unsigned int nr) 2 { 3 trace_softirq_raise(nr); 4 or_softirq_pending(1UL << nr); 5 } 6 7 typedef struct { 8 unsigned int __softirq_pending; 9 unsigned int __nmi_count; /* arch dependent */ 10 } irq_cpustat_t; 11 12 irq_cpustat_t irq_stat[]; 13 #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member) 14 #define or_softirq_pending(x) percpu_or(irq_stat.__softirq_pending, (x)) 15 #define local_softirq_pending() percpu_read(irq_stat.__softirq_pending) ?
喚醒ksoftirqd內核線程處理軟中斷。
1 static void wakeup_softirqd(void) 2 { 3 /* Interrupts are disabled: no need to stop preemption */ 4 struct task_struct *tsk = __get_cpu_var(ksoftirqd); 5 6 if (tsk && tsk->state != TASK_RUNNING) 7 wake_up_process(tsk); 8 }
在下列地方,待處理的軟中斷會被檢查和執行:
1. 從一個硬件中斷代碼處返回時
2. 在ksoftirqd內核線程中
3. 在那些顯示檢查和執行待處理的軟中斷的代碼中,如網絡子系統中
?
而不管是用什么方法喚起,軟中斷都要在do_softirq()中執行。如果有待處理的軟中斷,
do_softirq()會循環遍歷每一個,調用它們的相應的處理程序。
在中斷處理程序中觸發軟中斷是最常見的形式。中斷處理程序執行硬件設備的相關操作,
然后觸發相應的軟中斷,最后退出。內核在執行完中斷處理程序以后,馬上就會調用
do_softirq(),于是軟中斷開始執行中斷處理程序完成剩余的任務。
下面來看下do_softirq()的具體實現。
1 asmlinkage void do_softirq(void) 2 { 3 __u32 pending; 4 unsigned long flags; 5 6 /* 如果當前已處于硬中斷或軟中斷中,直接返回 */ 7 if (in_interrupt()) 8 return; 9 10 local_irq_save(flags); 11 pending = local_softirq_pending(); 12 if (pending) /* 如果有激活的軟中斷 */ 13 __do_softirq(); /* 處理函數 */ 14 local_irq_restore(flags); 15 }
?
1 /* We restart softirq processing MAX_SOFTIRQ_RESTART times, 2 * and we fall back to softirqd after that. 3 * This number has been established via experimentation. 4 * The two things to balance is latency against fairness - we want 5 * to handle softirqs as soon as possible, but they should not be 6 * able to lock up the box. 7 */ 8 asmlinkage void __do_softirq(void) 9 { 10 struct softirq_action *h; 11 __u32 pending; 12 /* 本函數能重復觸發執行的次數,防止占用過多的cpu時間 */ 13 int max_restart = MAX_SOFTIRQ_RESTART; 14 int cpu; 15 16 pending = local_softirq_pending(); /* 激活的軟中斷位圖 */ 17 account_system_vtime(current); 18 /* 本地禁止當前的軟中斷 */ 19 __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET); 20 lockdep_softirq_enter(); /* current->softirq_context++ */ 21 cpu = smp_processor_id(); /* 當前cpu編號 */ 22 23 restart: 24 /* Reset the pending bitmask before enabling irqs */ 25 set_softirq_pending(0); /* 重置位圖 */ 26 local_irq_enable(); 27 h = softirq_vec; 28 do { 29 if (pending & 1) { 30 unsigned int vec_nr = h - softirq_vec; /* 軟中斷索引 */ 31 int prev_count = preempt_count(); 32 kstat_incr_softirqs_this_cpu(vec_nr); 33 34 trace_softirq_entry(vec_nr); 35 h->action(h); /* 調用軟中斷的處理函數 */ 36 trace_softirq_exit(vec_nr); 37 38 if (unlikely(prev_count != preempt_count())) { 39 printk(KERN_ERR "huh, entered softirq %u %s %p" "with preempt_count %08x," 40 "exited with %08x?\n", vec_nr, softirq_to_name[vec_nr], h->action, prev_count, 41 preempt_count()); 42 } 43 rcu_bh_qs(cpu); 44 } 45 h++; 46 pending >>= 1; 47 } while(pending); 48 49 local_irq_disable(); 50 pending = local_softirq_pending(); 51 if (pending & --max_restart) /* 重復觸發 */ 52 goto restart; 53 54 /* 如果重復觸發了10次了,接下來喚醒ksoftirqd/n內核線程來處理 */ 55 if (pending) 56 wakeup_softirqd(); 57 58 lockdep_softirq_exit(); 59 account_system_vtime(current); 60 __local_bh_enable(SOFTIRQ_OFFSET); 61 } ?
(4) ksoftirqd內核線程
內核不會立即處理重新觸發的軟中斷。
當大量軟中斷出現的時候,內核會喚醒一組內核線程來處理。
這些線程的優先級最低(nice值為19),這能避免它們跟其它重要的任務搶奪資源。
但它們最終肯定會被執行,所以這個折中的方案能夠保證在軟中斷很多時用戶程序不會
因為得不到處理時間而處于饑餓狀態,同時也保證過量的軟中斷最終會得到處理。
?
每個處理器都有一個這樣的線程,名字為ksoftirqd/n,n為處理器的編號。
1 static int run_ksoftirqd(void *__bind_cpu) 2 { 3 set_current_state(TASK_INTERRUPTIBLE); 4 current->flags |= PF_KSOFTIRQD; /* I am ksoftirqd */ 5 6 while(! kthread_should_stop()) { 7 preempt_disable(); 8 9 if (! local_softirq_pending()) { /* 如果沒有要處理的軟中斷 */ 10 preempt_enable_no_resched(); 11 schedule(); 12 preempt_disable(): 13 } 14 15 __set_current_state(TASK_RUNNING); 16 17 while(local_softirq_pending()) { 18 /* Preempt disable stops cpu going offline. 19 * If already offline, we'll be on wrong CPU: don't process. 20 */ 21 if (cpu_is_offline(long)__bind_cpu))/* 被要求釋放cpu */ 22 goto wait_to_die; 23 24 do_softirq(); /* 軟中斷的統一處理函數 */ 25 26 preempt_enable_no_resched(); 27 cond_resched(); 28 preempt_disable(); 29 rcu_note_context_switch((long)__bind_cpu); 30 } 31 32 preempt_enable(); 33 set_current_state(TASK_INTERRUPTIBLE); 34 } 35 36 __set_current_state(TASK_RUNNING); 37 return 0; 38 39 wait_to_die: 40 preempt_enable(); 41 /* Wait for kthread_stop */ 42 set_current_state(TASK_INTERRUPTIBLE); 43 while(! kthread_should_stop()) { 44 schedule(); 45 set_current_state(TASK_INTERRUPTIBLE); 46 } 47 48 __set_current_state(TASK_RUNNING); 49 return 0; 50 }
?
轉載于:https://www.cnblogs.com/aaronLinux/p/6341590.html
總結
以上是生活随笔為你收集整理的[Linux内核]软中断与硬中断的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: shell中十种实现自加的方法
- 下一篇: Markdown入门简介