workqueue

lethe1203發表於2024-04-01
workqueue作為中斷下半部的一種實現方式,和tasklet不同點在於:
1、workqueue中的工作項的執行是在核心執行緒的上下文中進行的,因此可以執行長時間執行的任務,不會阻塞其他程序的執行。tasklet 在中斷上下文中執行,因此不能執行可能會阻塞的操作或者長時間執行的任務。它們的執行時間應該非常短。
2、workqueue 適用於需要延遲執行且較長時間的任務,具有較低的優先順序和較高的靈活性。tasklet 適用於需要立即執行且執行時間很短的任務,具有較高的優先順序,但數量受限且只能在中斷上下文中執行。

workqueue相關結構體:

1、work_struct:工作佇列中要執行的工作項的結構體。它的定義如下:

struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
};

// 引數說明
data:傳遞給工作函式的引數。
entry:連結串列節點,用於將工作項連線到工作佇列中。
func:指向實際工作函式的指標。

2、workqueue_struct:工作佇列的結構體,表示一個工作佇列。它的定義如下:

struct workqueue_struct {
    struct list_head list;
    const char *name;
    struct lock_class_key key;
    struct lockdep_map lockdep_map;
};

// 引數說明
list:用於連線所有工作佇列的連結串列節點。
name:工作佇列的名稱。
key:用於鎖的類別關鍵字。
lockdep_map:用於鎖依賴關係的對映。

workqueue相關函式:

1、INIT_WORK:用於初始化工作項結構體。它的原型如下:

void INIT_WORK(struct work_struct *work, work_func_t func);

// 引數說明
work:指向要初始化的工作項結構體的指標。
func:指向實際工作函式的指標。

2、schedule_work:用於安排工作項在適當的時候執行。它的原型如下:

bool schedule_work(struct work_struct *work);

// 引數說明
work:要安排執行的工作項。

3、flush_workqueue:用於重新整理工作佇列,確保佇列中的所有工作項都被執行完畢。它的原型如下:

int flush_workqueue(struct workqueue_struct *wq);

// 引數說明
wq:要重新整理的工作佇列。

4、create_workqueue:用於建立一個新的工作佇列。在較新的核心版本中,該函式已被棄用,可以使用 alloc_workqueue 代替。它的原型如下:

struct workqueue_struct *create_workqueue(const char *name);

// 引數說明
name:工作佇列的名稱。

5、alloc_workqueue:用於分配並初始化一個新的工作佇列。它的原型如下:

struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags, int max_active);

// 引數說明
fmt:工作佇列名稱的格式字串。
flags:工作佇列的標誌。
max_active:工作佇列中同時執行的最大工作項數。

6、destroy_workqueue:用於銷燬一個工作佇列,並釋放其相關資源。它的原型如下:

void destroy_workqueue(struct workqueue_struct *wq);

// 引數說明
wq:要銷燬的工作佇列。

workqueue driver demo如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>

#define WORK_QUEUE_NAME "my_workqueue"

// 定義工作結構體
struct work_struct my_work;

// 工作處理函式
void my_work_handler(struct work_struct *work) {
    printk(KERN_INFO "Executing work handler function\n");
    // 在這裡執行下半部處理的任務
}

// 定義中斷處理函式
irqreturn_t irq_handler(int irq, void *dev_id) {
    printk(KERN_INFO "Interrupt handler\n");

    // 安排工作佇列執行下半部處理
    schedule_work(&my_work);

    return IRQ_HANDLED;
}

static int __init my_init(void) {
    int irq = 1; // 替換為實際的中斷號

    // 初始化工作佇列
    INIT_WORK(&my_work, my_work_handler);

    // 註冊中斷處理函式
    if (request_irq(irq, irq_handler, IRQF_SHARED, "my_irq_handler", &irq_handler) < 0) {
        printk(KERN_INFO "Failed to register interrupt handler\n");
        return -EFAULT;
    }

    printk(KERN_INFO "Interrupt handler registered\n");
    return 0;
}

static void __exit my_exit(void) {
    int irq = 1; // 替換為實際的中斷號

    // 取消註冊中斷處理函式
    free_irq(irq, &irq_handler);

    // 重新整理工作佇列,確保已經執行了所有排隊的工作
    flush_workqueue(system_long_wq);

    printk(KERN_INFO "Interrupt handler unregistered\n");
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lethe1203");