在由內核執行的幾個任務之間有些不是緊急的,在必要情況下他們可以延遲一段時間。一個中斷處理程序的幾個中斷服務例程之間是串行執行的,並且通常在一個中斷的處理程序結束前,不應該再次出現這個中斷。相反,可延遲中斷可以在開中斷的情況下執行。
linux中所謂的可延遲函數,包括軟中斷和tasklet以及通過中作隊列執行的函數(這個以後說),軟中斷的分配是靜態的(即值編譯時定義),而tasklet的分配和初始化可以在運行時進行。
軟中斷
軟中斷所使用的數據結構定義為
- static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
其中softirq_action類型為一個函數指針,從這裡也可以看出,軟中斷的個數是有限的有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, /* Preferable RCU should always be the last softirq */
-
- NR_SOFTIRQS
- };
可以看出,一共10個軟中斷。
- struct softirq_action
- {
- void (*action)(struct softirq_action *);
- };
軟中斷的初始化
- /*初始化軟中斷*/
- void open_softirq(int nr, void (*action)(struct softirq_action *))
- {
- softirq_vec[nr].action = action;
- }
上面函數中,參數nr為softirq_vec[]數組的下標,初始化就是初始化softirq_vec[]數組內容。
初始化了軟中斷後,要執行,接下來要做的是激活軟中斷,運用下面函數
- /*激活軟中斷*/
- void raise_softirq(unsigned int nr)
- {
- unsigned long flags;
- /*保存eflags寄存器IF標志的狀態值
- 並禁用本地CPU上得中斷*/
- local_irq_save(flags);
-
- raise_softirq_irqoff(nr);
- local_irq_restore(flags);
- }
具體的激活工作由raise_softirq_irqoff函數實現
- /*
- * This function must run with irqs disabled!
- */
- inline void raise_softirq_irqoff(unsigned int nr)
- {
- /*把軟中斷標記為掛起*/
- __raise_softirq_irqoff(nr);
-
- /*
- * If we're in an interrupt or softirq, we're done
- * (this also catches softirq-disabled code). We will
- * actually run the softirq once we return from
- * the irq or softirq.
- *
- * Otherwise we wake up ksoftirqd to make sure we
- * schedule the softirq soon.
- */
- if (!in_interrupt())
- wakeup_softirqd();/*喚醒本地的內核線程*/
- }
守護線程softirqd就是對軟中斷的處理
- static int ksoftirqd(void * __bind_cpu)
- {
- /*設置進程狀態為可中斷*/
- set_current_state(TASK_INTERRUPTIBLE);
-
- while (!kthread_should_stop()) {/*不應該馬上返回*/
- preempt_disable();
- /*實現軟中斷中一個關鍵數據結構是每個
- CPU都有的32位掩碼(描述掛起的軟中斷),
- 他存放在irq_cpustat_t數據結構的__softirq_pending
- 字段中。為了獲取或設置位掩碼的值,
- 內核使用宏local_softirq_pending,他選擇cpu的
- 軟中斷為掩碼*/
- if (!local_softirq_pending()) {/*位掩碼為0,標示沒有軟中斷*/
- preempt_enable_no_resched();
- schedule();
- preempt_disable();
- }
-
- __set_current_state(TASK_RUNNING);
-
- while (local_softirq_pending()) {
- /* Preempt disable stops cpu going offline.
- If already offline, we'll be on wrong CPU:
- don't process */
- if (cpu_is_offline((long)__bind_cpu))
- goto wait_to_die;
- do_softirq();/*調用軟中斷處理函數*/
- preempt_enable_no_resched();
- cond_resched();
- preempt_disable();
- rcu_sched_qs((long)__bind_cpu);
- }
- preempt_enable();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- __set_current_state(TASK_RUNNING);
- return 0;
-
- wait_to_die:
- preempt_enable();
- /* Wait for kthread_stop */
- set_current_state(TASK_INTERRUPTIBLE);
- while (!kthread_should_stop()) {
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- __set_current_state(TASK_RUNNING);
- return 0;
- }