硬中斷和軟中斷(轉)

來自:http://blog.csdn.net/zhangskd/article/details/21992933java

 

記錄一個軟中斷問題:http://huoding.com/2013/10/30/296

 

概述

 

從本質上來說,中斷是一種電信號,當設備有某種事件發生時,它就會產生中斷,經過總線把電信號發送給中斷控制器。linux

若是中斷的線是激活的,中斷控制器就把電信號發送給處理器的某個特定引腳。處理器因而當即中止本身正在作的事,數組

跳到中斷處理程序的入口點,進行中斷處理。網絡

 

(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) 註冊中斷處理函數

註冊中斷處理函數:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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);  

 

中斷處理函數自己:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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) 註銷中斷處理函數

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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結構體表示:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. struct softirq_action {  
  2.     void (*action) (struct softirq_action *); /* 軟中斷的處理函數 */  
  3. };  

 

目前已註冊的軟中斷有10種,定義爲一個全局數組:

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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) 註冊軟中斷處理函數

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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()來觸發軟中斷。

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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是等待處理的軟中斷的位圖,經過設置此變量

便可告訴內核該執行哪些軟中斷。

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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內核線程處理軟中斷。

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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()的具體實現。

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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. }  
[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  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爲處理器的編號。

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
    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;  
相關文章
相關標籤/搜索