天天看點

linux軟體中斷——tasklet機制

轉自:http://liu1227787871.blog.163.com/blog/static/20536319720129210112658/

注意:軟體中斷不是軟中斷。軟體中斷不依賴于底層架構,類似于信号機制;而軟中斷依賴于底層架構,采用特殊的指令産生,系統調用就是采用的軟中斷,ARM架構下使用SWI指令産生軟中斷。

本文隻是對tasklet作一個粗淺的解釋,不對之處敬請指正!

本節我們先來說一下為什麼要引入軟體中斷?

一般來說,一次中斷服務的過程通常可以分為兩個部分。開頭的 部分往往必須在關中斷的條件下執行,這樣才能在不受幹擾的條件下“原子”地完成一些關鍵性操作,同時這部分操作的時間性又往往很強,必須在中斷請求發生後立即或至少在一定時間限制中執行,而且相繼的多次中斷請求也不能合并在一起來處理。而後半部分,通常可以而且應該在開中斷的條件下執行,這樣才不至于因中斷關閉過久而造成其他中斷的丢失,同時,這些操作常常允許延時到稍後才來執行,而且有可能多次中斷的相關部分合并在一起處理。這些不同的性質常常使中斷服務的前後兩半明顯地區分開來,可以而且應該分别加以不同的實作。這裡的後半部分就稱為"bottom half",在核心代碼中往往寫成bf ,而bf的這部分就可以通過軟體中斷來實作。因為軟體中斷的激活是通過代碼來實作的,而不是硬體,是以就可以自己或由系統來決定激活的時機!

那麼軟體中斷具體在什麼時候激活呢? 我們一般認為有兩個時機: 第一個時機:do_IRQ完成後,在退出的時候調用irq_exit do_IRQ       irq_exit();             if (!in_interrupt() && local_softirq_pending()) //如果不處在中斷上下文并且軟體中斷開啟的話,就激活軟體中斷                  invoke_softirq();                       do_softirq();                             __do_softirq();                                  h = softirq_vec;                                  h->action(h); 第一個時機一般可以處理大部分的軟體中斷,但是如果軟體中斷過多的話,不能在這個時機完成,那麼别擔心,還有第二個時機!

第二處:如果在irq_exit中沒有完成軟體中斷的話,會調用背景守護程序ksoftirqd 我們得先看一看守護程序的建立: early_initcall(spawn_ksoftirqd); spawn_ksoftirqd       cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);               p = kthread_create_on_node(run_ksoftirqd, hcpu,cpu_to_node(hotcpu), "ksoftirqd/%d", hotcpu); 這裡建立了核心線程 run_ksoftirqd  我們看看調用核心線程,我們看看如何激活呢? raise_softirq       raise_softirq_irqoff(nr);              wakeup_softirqd();

                    struct task_struct *tsk = __this_cpu_read(ksoftirqd);                     wake_up_process(tsk); 我們看看這個線程做了什麼: run_ksoftirqd       __do_softirq();            h->action(h) ;//果然

當然還有網卡驅動用到的軟體中斷,我們先不說! 以上所謂的排程時機,實際上并不是軟中斷必須的,而是核心中基于軟中斷的tasklet機制所實作的!軟中斷說白了就是先寫一個函數,然後在我們想執行這個函數的時候去激活它,我們完全可以自己來規劃其激活時機!但是事實上,軟體的開發人員并不建議我們自己注冊新的軟體中斷,核心為我們搭建了一個使用軟體中斷的架構,這就是tasklet機制,而tasklet機制正是為了實作中斷的下半部! 那麼要使用這個機制我們需要做些什麼呢? 1、建立 DECLARE_TASKLET(name, func, data) 我們可以看看其定義: #define DECLARE_TASKLET(name, func, data)  struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

由此我們也可以看出,我們也能直接這樣定義: struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

同時我們還得構造func,這就是然間中斷的執行函數!

2、初始化: void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data) { t->next = NULL; t->state = 0; atomic_set(&t->count, 0); t->func = func; t->data = data; } 隻是完成了指派的工作!

3、排程 static inline void tasklet_schedule(struct tasklet_struct *t) { if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) __tasklet_schedule(t); }

tasklet_schedule       __tasklet_schedule(t);             raise_softirq_irqoff(TASKLET_SOFTIRQ);                  __raise_softirq_irqoff(nr);//開啟軟中斷                         or_softirq_pending(1UL << nr);                 if (!in_interrupt())                     wakeup_softirqd();                          struct task_struct *tsk = __this_cpu_read(ksoftirqd);                          if (tsk && tsk->state != TASK_RUNNING)                               wake_up_process(tsk); //開啟守護程序,就運作了軟體中斷處理函數! tasklet_schedule這個函數裡面我們有可能去執行軟體中斷,但是并不一定,如果處于中斷上下文,或者已經運作,就會立即傳回,是以這個函數做的最有意義的一件事情就是__raise_softirq_irqoff(nr),開啟了中斷! 就先跟我們上面分析的一樣,排程的時機有以上兩種。

但是現在還是有點糊塗,tasklet軟體中斷到底是如何挂鈎的呢,我們就要從軟體中斷的初始化來說起了: softirq_init();       open_softirq(TASKLET_SOFTIRQ, tasklet_action);       open_softirq(HI_SOFTIRQ, tasklet_hi_action); //這裡注冊了兩個軟中斷 當do_softirq激活軟中斷的時候,實際上是調用tasklet_action這個函數,在由tasklet_action來調用我們在tasklet機制實作的處理函數,我們進入tasklet_action函數,看到它會周遊連結清單,來執行func函數!

繼續閱讀