天天看點

linux中斷底半部

ISR        Interrupt Service Routines(中斷服務程式)

INTC    Interrupt controller(中斷控制器)

Linux中通常分為外部中斷(又叫硬體中斷)和内部中斷(又叫異常)

軟體對硬體進行配置後,軟體期望等待硬體的某種狀态(比如,收到了資料),這裡有兩種方式,

一種是輪詢(polling): CPU 不斷的去讀硬體狀态。另一種是當硬體完成某種事件後,給 CPU 

一個中斷,讓 CPU 停下手上的事情,去處理這個中斷。很顯然,中斷的互動方式提高了系統的吞吐。

當 CPU 收到一個中斷 (IRQ)的時候,會去執行該中斷對應的處理函數(ISR)。普通情況下,

會有一個中斷向量表,向量表中定義了 CPU 對應的每一個外設資源的中斷處理程式的入口,當

發生對應的中斷的時候, CPU 直接跳轉到這個入口執行程式。也就是中斷上下文。(注意:中斷上下文中,不可阻塞睡眠)。

在Linux中希望盡快完成ISR,但是有些ISR事務繁重,會消耗很多時間,導緻反應速度變差,linux針對這種情況,分了上半部,底半部。

    1. 上半部(top half):收到一個中斷,立即執行,有嚴格的時間限制,隻做一些必要的工作,比如:應答,複位等。這些工作都是在所有中斷被禁止的情況下完成的。

    2. 底半部(bottom half):能夠被推遲到後面完成的任務會在底半部進行。在适合的時機,下半部會被開中斷執行。

相關API:

    申請注冊一個中斷處理函數:

    request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)

        參數    含義

        irq        表了該中斷的中斷号,一般 CPU 的中斷号都會事先定義好。

        handler    中斷發生後的 ISR

        flags    中斷标志( IRQF_DISABLED / IRQFSAMPLE_RANDOM / IRQF_TIMER / IRQF_SHARED)

            标志    含義

            IRQF_DISABLED    設定這個标志的話,意味着核心在處理這個 ISR 期間,要禁止其他中斷(多數情況不使用這個)

            IRQFSAMPLE_RANDOM    表明這個裝置産生的中斷對核心熵池有貢獻

            IRQF_TIMER    為系統定時器準備的标志

            IRQF_SHARED    表明多個中斷處理程式之間共享中斷線。同一個給定的線上注冊每個處理程式,必須設定這個    

        name     中斷相關的裝置 ASCII 文本,例如 "keyboard",這些名字會在 /proc/irq 和 /proc/interrupts 檔案使用

        dev        用于共享中斷線,傳遞驅動程式的裝置結構。非共享類型的中斷,直接設定成為 NULL

    return:0 success    -EBUSY表示中斷線已使用或者沒有指定IRQF_SHARED

    注意:request_irq 函數可能引起睡眠,是以不允許在中斷上下文或者不允許睡眠的代碼中調用。

    釋放中斷

    const void *free_irq(unsigned int irq, void *dev_id)

實作底半部的三種方法:tasklet,等待隊列,軟中斷

    軟中斷:很少用于實作下半部,但是tasklet是軟中斷實作的。軟中斷就是軟體實作的異步中斷,優先級低于硬體中斷,高于普通程序。

            軟中斷是在編譯時候靜态配置設定的,要用軟中斷就要修改核心代碼。

        實作軟中斷的相關結構體和函數:

            struct softirq_action

            {

                void (*action)(struct softirq_action *); //軟中斷處理函數

            };

            enum    //軟中斷的優先級别

            {

                HI_SOFTIRQ=0, //用于tasklet的軟中斷,優先級最高,為0            

                TIMER_SOFTIRQ, //定時器的下半部            

                NET_TX_SOFTIRQ, //發送網絡資料的軟中斷            

                NET_RX_SOFTIRQ, //接受網絡資料的軟中斷            

                BLOCK_SOFTIRQ,            

                TASKLET_SOFTIRQ, //也是用于實作tasklet            

                SCHED_SOFTIRQ,            

                HRTIMER_SOFTIRQ,            

                RCU_SOFTIRQ,         

                XIAOBAI_SOFTIRQ, //這是自添加軟中斷的一個中斷号,優先級最低

                NR_SOFTIRQS, //這個就是上面所說的軟中斷結構體數組成員個數

            };

            static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;    //聲明軟中斷的一個表

            void open_softirq(int nr, void (*action)(struct softirq_action *))        //申請注冊一個軟中斷(中斷号綁定執行函數)

            注意:如果想自添加軟中斷,需要導出符将open_softirq導出,在其他驅動中可以調用。EXPORT_SYMBOL(open_softirq);

            void raise_softirq(unsigned int nr)    //觸發對應的軟中斷,參數為枚舉的中斷号XIAOBAI_SOFTIRQ

            注意:如果想自添加軟中斷,需要導出符将raise_softirq導出,在其他驅動中可以調用。EXPORT_SYMBOL(raise_softirq);

            實作流程為:

                1.在include/linux/interrupt.h中添加自己的軟中斷号XIAOBAI_SOFTIRQ(在RCU_SOFTIRQ後添加)

                2.将kernel/softirq.c中open_softirq和raise_softirq兩個函數使用導出符EXPORT_SYMBOL導出。

                3.申請一個軟中斷并綁定處理函數    open_softirq(XIAOBAI_SOFTIRQ,handler_function);

                4.在中斷處理函數中執行下半部,也就是軟中斷。raise_softirq(XIAOBAI_SOFTIRQ);

        軟中斷補充:所謂的觸發軟中斷,并不是立刻觸發,他隻是告訴核心,下次執行軟中斷的時候,記得執行我的軟中斷處理函數。

            軟中斷優先級最高的中斷号就是tasklet。

    tasklet:

        和軟中斷一樣,觸發函數執行後并不是立刻執行,隻是告訴核心,下次執行軟中斷的時候執行我的處理函數。

        實作方法:

            1. struct tasklet_struct test_tasklet; //定義tasklet結構體

            2. tasklet_init(&test_tasklet, test_func, (unsigned long)123);    //test_func為處理函數,123為給處理函數傳的參數 

            3. tasklet_schedule(&test_tasklet);或者tasklet_hi_schedule(&test_tasklet);//中斷傳回前排程tasklet

                前者使用TASKLET_SOFTIRQ優先級,後者使用HI_SOFTIRQ優先級。

            4.移除tasklet的API:void tasklet_kill(struct tasklet_struct *t)//如果tasklet在運作,程式會休眠,等到它執行完畢。

            還有禁止和激活tasklet的函數:(禁止後,tasklet就不能用了,直到激活)

                static inline void tasklet_disable(struct tasklet_struct *t) //禁止

                static inline void tasklet_enable (struct tasklet_struct *t) //激活

        補充:軟中斷實作tasklet的中斷号優先級有TASKLET_SOFTIRQ和HI_SOFTIRQ兩種。

            一般使用tasklet_schedule會使用TASKLET_SOFTIRQ優先級5    (include/linux/interrupt.h中檢視優先級)

            使用tasklet_hi_schedule 會使用HI_SOFTIRQ優先級0

        總結:軟中斷的優點:優先級比普通的程序高,排程速度快。

              軟中斷的缺點:處于中斷上下文,是以不能睡眠

    工作隊列:

        工作隊列的執行上下文是核心線程,是以可以排程和睡眠。這就意味着需要擷取大量記憶體時、需要擷取信号量時、

        阻塞IO時,工作隊列比較有效。

        實作方法:

            1.struct workqueue_struct test_wq; //定義工作隊列

            2.實作工作隊列處理函數test_func();    

            3.INIT_WORK(&test_wq,test_func);//初始化工作隊列

                DECLARE_WROK(name,func);//定義并初始化工作隊列

            4.schedule_work(&test_wq);    //排程工作隊列,成功傳回1

                schedule_delayed_work(&test_wq,delay);    //延遲排程

總結:

    1,需要睡眠,阻塞的,隻能用工作隊列。

    2,短時間内中斷數量很多的,任務隊列,軟中斷會更好。例如網絡。

    3,對性能要求很高的話,軟中斷最好。

    4,使用任務隊列時,應該注意同一個任務被多次調用,同一個函數被多個任務隊列注意。

    5,軟中斷要注意SMP,函數的重入。

------------------------------------------------------------------------------------------------------------

|                    |tasklet                        |    軟中斷                        |    工作隊列

|-----------------------------------------------------------------------------------------------------------    

|SMP             |向一個CPU注冊            |多個CPU并發                    |    預設同一個CPU上或指定CPU

|                    |                                |                                |(queue_delayed_work_on)    

|-----------------------------------------------------------------------------------------------------------        

|上下文                |中斷                            |中斷                            |程序

|函數                |1.同類型隻能執行一個            |1.不能阻塞,睡眠                |1.可阻塞,睡眠

|                    |2.不能阻塞睡眠                    |2.不帶參數                        |2.不帶參數

|                    |3.可帶參數                        |3.ksoftirqd核心線程            |    3.events/0核心線程

|                    |4.ksoftirqd核心線程            |                                |

|-----------------------------------------------------------------------------------------------------------                    

|執行                |1.單個tasklet串行                |1.同一個CPU上串行                |1.程序排程内,可重新排程

|                    |2.同一個tasklet,隻響應最後一次|    2.不同的CPU并行                |    

|                    |                                |3.不能搶占另一軟中斷            |

|                    |3.多個之間為并行                |                                |

|-----------------------------------------------------------------------------------------------------------            

|atomic_context        |原子模式執行                    |原子模式執行                    |不是

|-----------------------------------------------------------------------------------------------------------

|響應時間            |1.tasklet_hi_schedule更早        |1.最遲在下一個time tick時,    |    可指定延後時間,jiffies機關(1ms-百ms)

|                    |2.最遲在下一個time tick時,    |中斷中調用,就是中斷完成        |核心線程,上下文切換

|                    |中斷中調用,就是中斷完成        |後立即執行                        |

|                    |後立即執行                        |                                |

|-----------------------------------------------------------------------------------------------------------

|開銷                |次之(與軟中斷接近)            |最小                            |開銷最大

繼續閱讀