Linux Kernel中断下半部分实现的三种方式
👉👉👉 個人博客筆記導讀目錄(全部) 👈👈👈
.
說明:
在默認情況下,本文講述的都是ARMV8-aarch64架構,linux kernel 5.14
目錄
- 1、軟中斷
- 2、tasklet
- 3、工作隊列
- 總結
目前有三種中斷的三種機制:
- 軟中斷
- tasklet
- 工作隊列
1、軟中斷
軟中斷是一組靜態定義的下半部接口,有 32 個,可以在所有處理器上同時執行,類型相同也可以;在編譯時靜態注冊。
軟中斷的相關函數:
- 注冊軟中斷 open_softirq
- 觸發軟中斷 raise_softirq
- 執行軟中斷 do_softirq
Linux Kernel中定義的軟中斷:
(linux/include/linux/interrupt.h)enum {HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,BLOCK_SOFTIRQ,IRQ_POLL_SOFTIRQ,TASKLET_SOFTIRQ,SCHED_SOFTIRQ,HRTIMER_SOFTIRQ,RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */NR_SOFTIRQS };軟中斷執行函數如下:
(linux/kernel/softirq.c)asmlinkage __visible void do_softirq(void){__u32 pending;unsigned long flags;if (in_interrupt())return;local_irq_save(flags);pending = local_softirq_pending();if (pending && !ksoftirqd_running(pending))do_softirq_own_stack();local_irq_restore(flags);}代碼一上來就判斷是否在中斷處理中,如果在立刻退出函數。這說明如果有其他軟中斷觸發,則立即返回。所以,軟中斷不能被另外一個軟中斷搶占!唯一可以搶占軟中斷的是中斷處理程序,所以軟中斷允許響應中斷。雖然不能在本處理器上搶占,但是其他的軟中斷甚至同類型可以再其他處理器上同時執行。由于這點,所以對臨界區需要加鎖保護。
軟中斷給對時間要求最嚴格的下半部使用。目前只有網絡,內核定時器和 tasklet 建立在軟中斷上。
2、tasklet
Tasklet是建立在軟中斷之上的下半部機制,tasklet和軟中斷很類似,但是tasklet的接口更簡單,也不需要嚴格的鎖機制。因為tasklet是使用軟中斷來實現的,所以tasklet本身就是軟中斷。
tasklet使用兩種軟中斷來實現:HI_SOFTIRQ和TASKLET_SOFTIRQ。兩者的唯一區別在于優先級,前者優先級更高,總是先于后者執行。
tasklet使用tasklet_struct結構來表示,每個結構體表示一個唯一的tasklet,定義在<linux/interrupt.h>中
(linux/include/linux/interrupt.h)struct tasklet_struct {struct tasklet_struct *next;unsigned long state;atomic_t count;bool use_callback;union {void (*func)(unsigned long data);void (*callback)(struct tasklet_struct *t);};unsigned long data; };Tasklet的使用:
(1)、聲明一個新的tasklet
struct tasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0), my_tasklet_handler, dev };或者
tasklet_init(t, tasklet_handler, dev); /* dynamically as opposed to statically */(2)、tasklet的處理程序
void tasklet_handler(unsigned long data)注意:和軟中斷類似,tasklet不能睡眠(阻塞),因為軟中斷是運行在中斷上下文中的,而tasklet是使用軟中斷來實現的。
(3)、tasklet的調度
使用tasklet_schedule()進行調度(類似軟中斷的觸發),傳入參數為指向tasklet_struct的指針。tasklet被調度后,內核會在合適的時機執行該taskelt。(詳見前面的tasklet調度的實現)。如果一個tasklet在執行前被調度了多次,還是只會執行一次(tasklet鏈表中不會有重復的tasklet)。如果一個tasklet在運行中被調度了(比如被另一個處理器上執行的代碼調度了),那么這個tasklet會被重新調度并在下次內核處理tasklet的時候再次執行。
3、工作隊列
工作隊列是和軟中斷或者tasklet不同的一種下半部機制。工作隊列將工作推遲,交給內核線程執行(所以工作隊列總是運行在進程上下文中)。工作隊列的這種實現可以很好的利用進程上下文的優勢,最重要的就是可以睡眠也可以被調度(搶占)。與之相反的是,軟中斷和tasklet是不能睡眠和被調度的。
可以自己創建工作隊列,但是大部分驅動都會使用系統提供的缺省的工作隊列類型events,該類型的工作隊列的內核線程名字為 events/n,n為處理器編號,每個處理器對應一個內核線程。如果下半部的工作是處理器密集型并且對性能敏感的,可以考慮創建自己的內核線程。比如XFS文件系統就自己創建了兩種內核線程。
工作隊列的使用:–(缺省工作隊列events)
(1)、創建工作(Creaing Work)
DECLARE_WORK(name, void (*func)(void *), void *data); //靜態 INIT_WORK(struct work_struct *work, void (*func)(void *), void *data); //動態(2)、定義工作隊列處理函數
void work_handler(void *data)(3)、對工作(work)進行調度
內核提供了兩個函數對使用缺省工作隊列events的工作進行調度
schedule_work(&work); schedule_delayed_work(&work, delay);- schedule_work()會立刻對工作(work)進行調度,一旦其所在的處理器上的events內核線程被喚醒,該工作就會被執行。
- schedule_delayed_work()會延后一定數量的(由dealy指定)的timer tick后再進行調度。
創建自己的內核線程工作隊列
(1)、創建內核線程工作隊列
如果需要利用單獨的內核線程的(不用events的內核線程)的性能優勢,可以通過函數struct workqueue_struct *create_workqueue(const char *name)創建一個新的工作隊列,參數是工作隊列的名字。比如缺省的events工作隊列的創建:
這個函數會創建所有的內核線程(每個處理器一個),并且做些準備好讓這些內核線程可以處理工作
(2)、工作隊列調度
int queue_work(struct workqueue_struct *wq, struct work_struct *work) int queue_delayed_work(struct workqueue_struct *wq,struct work_struct *work,unsigned long delay)總結
| 軟中斷 | 中斷上下文 | 不能睡眠、不能被搶占 |
| tasklet | 中斷上下文 | 不能睡眠、不能搶占、同類型tasklet不能并行 |
| 工作隊列 | 進程上下文 | 可以睡眠、可以被搶占 |
總結
以上是生活随笔為你收集整理的Linux Kernel中断下半部分实现的三种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [工具]-PGP的使用方法
- 下一篇: [Issue Fixed]-repo-e