歡迎您光臨本站 註冊首頁

linux軟中斷

←手機掃碼閱讀     火星人 @ 2014-03-03 , reply:0

linux軟中斷


linux軟中斷



在由內核執行的幾個任務之間有些不是緊急的,在必要情況下他們可以延遲一段時間。一個中斷處理程序的幾個中斷服務常式之間是串列執行的,並且通常在一個中斷的處理程序結束前,不應該再次出現這個中斷。相反,可延遲中斷可以在開中斷的情況下執行。

linux中所謂的可延遲函數,包括軟中斷和tasklet以及通過中作隊列執行的函數(這個以後說),軟中斷的分配是靜態的(即值編譯時定義),而tasklet的分配和初始化可以在運行時進行。

軟中斷

軟中斷所使用的數據結構定義為



view plaincopy to clipboard01.static struct softirq_action softirq_vec __cacheline_aligned_in_smp;  其中softirq_action類型為一個函數指針,從這裡也可以看出,軟中斷的個數是有限的有NR_SOFTIRQS個,具體的定義如下:



view plaincopy to clipboard01.enum  
02.{  
03.    HI_SOFTIRQ=0,  
04.    TIMER_SOFTIRQ,  
05.    NET_TX_SOFTIRQ,  
06.    NET_RX_SOFTIRQ,  
07.    BLOCK_SOFTIRQ,  
08.    BLOCK_IOPOLL_SOFTIRQ,  
09.    TASKLET_SOFTIRQ,  
10.    SCHED_SOFTIRQ,  
11.    HRTIMER_SOFTIRQ,  
12.    RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */  
13.  
14.    NR_SOFTIRQS  
15.};  可以看出,一共10個軟中斷。



view plaincopy to clipboard01.struct softirq_action  
02.{  
03.    void    (*action)(struct softirq_action *);  
04.}; 軟中斷的初始化



view plaincopy to clipboard01./*初始化軟中斷*/  
02.void open_softirq(int nr, void (*action)(struct softirq_action *))  
03.{  
04.    softirq_vec.action = action;  
05.}  上面函數中,參數nr為softirq_vec[]數組的下標,初始化就是初始化softirq_vec[]數組內容。

初始化了軟中斷後,要執行,接下來要做的是激活軟中斷,運用下面函數



view plaincopy to clipboard01./*激活軟中斷*/  
02.void raise_softirq(unsigned int nr)  
03.{  
04.    unsigned long flags;  
05.    /*保存eflags寄存器IF標誌的狀態值
06.    並禁用本地CPU上得中斷*/  
07.    local_irq_save(flags);  
08.      
09.    raise_softirq_irqoff(nr);  
10.    local_irq_restore(flags);  
11.}  具體的激活工作由raise_softirq_irqoff函數實現



view plaincopy to clipboard01./*
02. * This function must run with irqs disabled!
03. */  
04.inline void raise_softirq_irqoff(unsigned int nr)  
05.{     
06.    /*把軟中斷標記為掛起*/  
07.    __raise_softirq_irqoff(nr);  
08.  
09.    /*
10.     * If we're in an interrupt or softirq, we're done
11.     * (this also catches softirq-disabled code). We will
12.     * actually run the softirq once we return from
13.     * the irq or softirq.
14.     *
15.     * Otherwise we wake up ksoftirqd to make sure we
16.     * schedule the softirq soon.
17.     */  
18.    if (!in_interrupt())  
19.        wakeup_softirqd();/*喚醒本地的內核線程*/  
20.}  
守護線程softirqd就是對軟中斷的處理



view plaincopy to clipboard01.static int ksoftirqd(void * __bind_cpu)  
02.{  
03.    /*設置進程狀態為可中斷*/  
04.    set_current_state(TASK_INTERRUPTIBLE);  
05.  
06.    while (!kthread_should_stop()) {/*不應該馬上返回*/  
07.        preempt_disable();  
08.        /*實現軟中斷中一個關鍵數據結構是每個
09.        CPU都有的32位掩碼(描述掛起的軟中斷),
10.        他存放在irq_cpustat_t數據結構的__softirq_pending
11.        欄位中。為了獲取或設置位掩碼的值,
12.        內核使用宏local_softirq_pending,他選擇cpu的
13.        軟中斷為掩碼*/  
14.        if (!local_softirq_pending()) {/*位掩碼為0,標示沒有軟中斷*/  
15.            preempt_enable_no_resched();  
16.            schedule();  
17.            preempt_disable();  
18.        }  
19.  
20.        __set_current_state(TASK_RUNNING);  
21.  
22.        while (local_softirq_pending()) {  
23.            /* Preempt disable stops cpu going offline.
24.               If already offline, we'll be on wrong CPU:
25.               don't process */  
26.            if (cpu_is_offline((long)__bind_cpu))  
27.                goto wait_to_die;  
28.            do_softirq();/*調用軟中斷處理函數*/  
29.            preempt_enable_no_resched();  
30.            cond_resched();  
31.            preempt_disable();  
32.            rcu_sched_qs((long)__bind_cpu);  
33.        }  
34.        preempt_enable();  
35.        set_current_state(TASK_INTERRUPTIBLE);  
36.    }  
37.    __set_current_state(TASK_RUNNING);  
38.    return 0;  
39.  
40.wait_to_die:  
41.    preempt_enable();  
42.    /* Wait for kthread_stop */  
43.    set_current_state(TASK_INTERRUPTIBLE);  
44.    while (!kthread_should_stop()) {  
45.        schedule();  
46.        set_current_state(TASK_INTERRUPTIBLE);  
47.    }  
48.    __set_current_state(TASK_RUNNING);  
49.    return 0;  
50.}  下面是軟中斷的執行



view plaincopy to clipboard01.
/*如果在這樣的一個檢查點(local_softirq_pending()不為0) 
  02.檢測到掛起的軟中斷,內核調用下面函數處理*/  
  03.asmlinkage void do_softirq(void)  
  04.{  
  05.    __u32 pending;  
  06.    unsigned long flags;  
  07.  
  08.    if (in_interrupt())  
  09.        return;  
  10.  
  11.    local_irq_save(flags);  
  12.  
  13.    pending = local_softirq_pending();  
  14.  
  15.    if (pending)  
  16.        __do_softirq();  
  17.  
  18.    local_irq_restore(flags);  
  19.}  
   具體由__do_softirq函數實現
   
  
  
  view plaincopy to clipboard01./*讀取本地CPU的軟中斷掩碼並執行與每個設置位 
  02.相關的可延遲函數,__do_softirq只做固定次數的循環 
  03.然後就返回。如果還有其餘掛起的軟中斷,那麼 
  04.內核線程ksofirqd將會在預期的時間內處理他們*/  
  05.asmlinkage void __do_softirq(void)  
  06.{  
  07.    struct softirq_action *h;  
  08.    __u32 pending;  
  09.    /*把循環計數器的值初始化為10*/  
  10.    int max_restart = MAX_SOFTIRQ_RESTART;  
  11.    int cpu;  
  12.    /*把本地CPU(被local_softirq_pending選中的)軟體中斷的 
  13.    位掩碼複製到局部變數pending中*/  
  14.    pending = local_softirq_pending();  
  15.    account_system_vtime(current);  
  16.    /*增加軟中斷計數器的值*/  
  17.    __local_bh_disable((unsigned long)__builtin_return_address(0));  
  18.    lockdep_softirq_enter();  
  19.  
  20.    cpu = smp_processor_id();  
  21.restart:  
  22.    /* Reset the pending bitmask before enabling irqs */  
  23.    set_softirq_pending(0);/*清除本地CPU的軟中斷點陣圖, 
  24.    以便可以激活新的軟中斷*/  
  25.  
  26.    /*激活本地中斷*/  
  27.    local_irq_enable();  
  28.  
  29.    h = softirq_vec;  
  30.  
  31.    do {/*根據pending每一位的的設置,執行對應的軟中斷 
  32.        處理函數*/  
  33.        if (pending & 1) {  
  34.            int prev_count = preempt_count();  
  35.            kstat_incr_softirqs_this_cpu(h - softirq_vec);  
  36.  
  37.            trace_softirq_entry(h, softirq_vec);  
  38.            h->action(h);/*執行註冊的具體的軟中斷函數*/  
  39.            trace_softirq_exit(h, softirq_vec);  
  40.            if (unlikely(prev_count != preempt_count())) {  
  41.                printk(KERN_ERR "huh, entered softirq %td %s %p"  
  42.                       "with preempt_count %08x,"  
  43.                       " exited with %08x?\n", h - softirq_vec,  
  44.                       softirq_to_name,  
  45.                       h->action, prev_count, preempt_count());  
  46.                preempt_count() = prev_count;  
  47.            }  
  48.  
  49.            rcu_bh_qs(cpu);  
  50.        }  
  51.        h++;  
  52.        pending >>= 1;  
  53.    } while (pending);  
  54.  
  55.    local_irq_disable();  
  56.  
  57.    pending = local_softirq_pending();  
  58.    if (pending && --max_restart)  
  59.        goto restart;  
  60.  
  61.    if (pending)/*如果還有掛起的軟中斷,喚醒內核線程 
  62.            來處理本地CPU的軟中斷*/  
  63.        wakeup_softirqd();  
  64.  
  65.    lockdep_softirq_exit();  
  66.  
  67.    account_system_vtime(current);  
  68.    _local_bh_enable();/*軟中斷計數器-1,因而重新激活可延遲函數*/  
  69.}  到此,linux內核軟中斷的大致執行和實現基本上分析完了,中間有很多地方沒有註釋的,主要是考慮到需要別的實現機制以及有的比較易懂。能夠自己看懂。
  				
《解決方案》

謝謝分享

[火星人 ] linux軟中斷已經有981次圍觀

http://coctec.com/docs/service/show-post-1320.html