Softirq和tasklet

lethe1203發表於2024-04-01

Softirq:

核心用softirq_action結構管理軟體中斷的註冊和啟用等操作,它的定義如下:
struct softirq_action
{
    void    (*action)(struct softirq_action *);
};
只有一個用於回撥的函式指標action。軟體中斷的資源是有限的,核心目前只實現了10種型別的軟中斷,它們是:
enum
{
    HI_SOFTIRQ=0,                  // 高優先順序軟中斷,通常用於高優先順序任務
    TIMER_SOFTIRQ,                 // 定時器軟中斷,用於處理定時器相關的事件
    NET_TX_SOFTIRQ,                // 網路傳輸軟中斷,處理網路資料傳送
    NET_RX_SOFTIRQ,                // 網路接收軟中斷,處理網路資料接收
    BLOCK_SOFTIRQ,                 // 塊裝置軟中斷,處理塊裝置相關的事件
    BLOCK_IOPOLL_SOFTIRQ,          // 塊裝置 I/O 輪詢軟中斷
    TASKLET_SOFTIRQ,               // 任務軟中斷,用於處理一些需要延遲執行的任務
    SCHED_SOFTIRQ,                 // 排程軟中斷,用於處理排程器相關的事件
    HRTIMER_SOFTIRQ,               // 高精度定時器軟中斷,處理高精度定時器事件
    RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */   // RCU(Read-Copy Update)軟中斷,用於處理 RCU 相關的事件
 
    NR_SOFTIRQS                    // 軟中斷的數量,用於表示軟中斷陣列的大小
};
如果個人想新增軟中斷,涉及修改中斷處理程式、軟中斷處理程式以及 Softirq 排程器等相關程式碼。所以不建議改動核心目前的10種型別軟中斷

tasklet相關函式:

1、DECLARE_TASKLET:這是一個宏,用於在核心中宣告一個 Tasklet。它的原型如下:

DECLARE_TASKLET(name, func, data);

//引數說明
name:Tasklet 的名稱。
func:Tasklet 的處理函式,當 Tasklet 被排程時執行。
data:傳遞給處理函式的引數。

2、tasklet_init:這個函式用於初始化一個 Tasklet 結構體。它的原型如下:

void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);

// 引數說明
t:指向要初始化的 Tasklet 結構體的指標。
func:Tasklet 的處理函式。
data:傳遞給處理函式的引數。

3、tasklet_schedule:這個函式用於安排 Tasklet 在適當的時候執行。它的原型如下:

void tasklet_schedule(struct tasklet_struct *t);

// 引數說明
t:要安排執行的 Tasklet。

4、tasklet_disable 和 tasklet_enable:這兩個函式用於禁用和啟用 Tasklet。禁用 Tasklet 後,即使呼叫 tasklet_schedule 也不會立即執行 Tasklet。它們的原型如下:

void tasklet_disable(struct tasklet_struct *t);
void tasklet_enable(struct tasklet_struct *t);

// 引數說明
t:要禁用或啟用的 Tasklet。

5、tasklet_kill:這個函式用於取消安排 Tasklet 的執行。它的原型如下:

void tasklet_kill(struct tasklet_struct *t);

// 引數說明
t:要取消的 Tasklet。

tasklet driver demo:

使用tasklet作為中斷下半部的demo如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>

#define IRQ_NUM 42

static int irq_counter = 0;
static struct tasklet_struct my_tasklet;

// 中斷處理程式的頂半部分
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    // 執行頂半部分的處理邏輯
    irq_counter++;

    // 排程 Tasklet 處理下半部分
    tasklet_schedule(&my_tasklet);

    return IRQ_HANDLED;
}

// Tasklet 的下半部分處理程式
static void my_tasklet_handler(unsigned long data)
{
    // 執行下半部分的處理邏輯
    pr_info("Tasklet executed. IRQ counter: %d\n", irq_counter);
}

static int __init my_module_init(void)
{
    int ret;

    // 初始化 Tasklet
    tasklet_init(&my_tasklet, my_tasklet_handler, 0);

    // 註冊中斷處理程式
    ret = request_irq(IRQ_NUM, my_interrupt_handler, IRQF_SHARED, "my_interrupt", NULL);
    if (ret) {
        pr_err("Failed to register IRQ handler\n");
        return ret;
    }

    pr_info("My module initialized\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    // 取消註冊中斷處理程式
    free_irq(IRQ_NUM, NULL);

    // 等待 Tasklet 完成並釋放資源
    tasklet_kill(&my_tasklet);

    pr_info("My module exited\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lethe1203");
MODULE_DESCRIPTION("tasklet demo");

相關文章