
提示
在上半部处理紧急的事情,在上半部的处理过程中,中断是被禁止的;在下半部处理耗时的事情,在下半部的处理过程中,中断是使能的。
一、什么是 tasklet
在 Linux 内核中,tasklet 是一种轻量级的软中断机制,广泛应用于处理与硬件中断相关的下半部任务(Bottom Half)。它的设计目标是提供一种高效且易于使用的机制,以确保在多核系统中避免并发问题。
1、Tasklet 的定义与结构
tasklet 的核心结构体 tasklet_struct 定义在头文件 include/linux/interrupt.h 中:

为了便于使用,内核还定义了类型别名 tasklet_t,使得开发者可以直接使用 tasklet_t 来声明 tasklet 变量。
2、结构体成员详解
next:该成员是一个指向下一个tasklet的指针,用于将多个tasklet组织成链表结构。state:用于表示tasklet的当前状态。TASKLET_STATE_SCHED:表示该tasklet已被调度,但尚未执行TASKLET_STATE_RUN:表示该tasklet正在某个 CPU 上运行
count:是一个引用计数器,用于管理tasklet的启用和禁用状态。func:是一个函数指针,指向tasklet绑定的回调函数。data:传递给func函数的参数。
注意
func 函数不得调用可能导致休眠的操作(如内存分配或等待事件),否则可能会导致内核异常。
二、tasklet 接口
1、静态初始化函数
DECLARE_TASKLET 宏的原型如下:

如果 tasklet 初始化函数为非使能状态,使用以下宏定义:

2、动态初始化函数
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data);2
参数解析:
t:指向一个struct tasklet_struct类型的指针。func:函数指针,指向 tasklet 的处理函数。data:传递给处理函数的参数。
完整示例代码:
#include <linux/interrupt.h>
#include <linux/module.h>
void my_tasklet_handler(unsigned long data)
{
printk(KERN_INFO "Tasklet handler executed with data: %lu\n", data);
}
struct tasklet_struct my_tasklet;
static int __init my_module_init(void)
{
tasklet_init(&my_tasklet, my_tasklet_handler, 12345);
tasklet_schedule(&my_tasklet);
printk(KERN_INFO "Tasklet initialized and scheduled.\n");
return 0;
}
static void __exit my_module_exit(void)
{
tasklet_kill(&my_tasklet);
printk(KERN_INFO "Tasklet destroyed.\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
3、关闭函数
void tasklet_disable(struct tasklet_struct *t);注意
关闭 tasklet 并不会销毁 tasklet 结构体,因此可以随时通过调用 tasklet_enable 函数重新启用 tasklet,或者调用 tasklet_kill 函数来销毁 tasklet。
4、使能函数
void tasklet_enable(struct tasklet_struct *t);5、调度函数
void tasklet_schedule(struct tasklet_struct *t);6、销毁函数
void tasklet_kill(struct tasklet_struct *t);三、tasklet 内部机制
tasklet 属于 TASKLET_SOFTIRQ 软件中断,入口函数为 tasklet_action,这在内核 kernel/softirq.c 中设置:

当驱动程序调用 tasklet_schedule 时,会设置 tasklet 的 state 为 TASKLET_STATE_SCHED,并把它放入某个链表:
文件位置
include/linux/interrupt.h



当发生硬件中断时,内核处理完硬件中断后,会处理软件中断。对于 TASKLET_SOFTIRQ 软件中断,会调用 tasklet_action 函数。
三个关键点
tasklet_schedule调度 tasklet 时,其中的函数并不会立刻执行,而只是把 tasklet 放入队列;- 调用一次
tasklet_schedule,只会导致 tasklet 的函数被执行一次; - 如果 tasklet 的函数尚未执行,多次调用
tasklet_schedule也是无效的,只会放入队列一次。
我们 softirq 初始化的时候指定服务程序:

tasklet_action 函数解析如下:

