11.1 概述
Tasklet是Linux内核中的一种机制,用于延迟执行任务。它们是基于软中断(softirq)实现的,运行在软中断上下文中。这意味着它们不能睡眠或阻塞,必须是原子操作。
11.2 Tasklet用途
Tasklet的主要目的是将一些不紧急的工作从中断处理程序中移出,以减少中断处理程序的执行时间。这有助于提高系统的响应速度和整体性能。通过将较长时间的处理任务延迟到稍后执行,Tasklet可以确保中断处理程序尽快完成。
11.3 使用场合
• 中断处理:当中断处理程序需要执行较长时间的任务时,可以使用Tasklet将这些任务延迟执行。
• 非紧急任务:适用于那些不需要立即执行的任务,可以在稍后合适的时间执行。
11.4 Tasklet注意事项
1). 原子操作:Tasklet运行在软中断上下文中,必须是原子操作,不能睡眠或阻塞。
2). 执行时间:保持Tasklet的执行时间尽可能短,以减少对系统性能的影响。
3). 数据同步:如果Tasklet需要访问共享数据,必须使用适当的同步机制(如自旋锁)来保护数据。
11.5 Tasklet如何使用
静态声明和初始化
DECLARE_TASKLET(my_tasklet, demo_tasklet_handler, data);
动态创建
static int __init demo_tasklet_init(void)
{
demo_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
if (!demo_tasklet)
{
printk(KERN_ERR "Failed to allocate memory for tasklet\n");
return -ENOMEM;
}
tasklet_init(demo_tasklet, demo_tasklet_handler, 0);
return 0;
}
上面是2种方法来创建tasklet, 在实际应用时, 任意一种都可以。
调度
tasklet_schedule(&my_tasklet);
处理函数
void demo_tasklet_handler(unsigned long data) {
// TODO: add code here to handle tasks...
}
11.6 Tasklet 驱动实例
创建一个tasklet.c文件, 内容如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h> // for kmalloc and kfree
static struct kobject *demo_kobj;
static void demo_tasklet_handler(unsigned long data);
static struct tasklet_struct *demo_tasklet;
static void demo_tasklet_handler(unsigned long data)
{
printk(KERN_INFO "Tasklet handler executed\n");
}
static int __init demo_tasklet_init(void)
{
demo_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
if (!demo_tasklet)
{
printk(KERN_ERR "Failed to allocate memory for tasklet\n");
return -ENOMEM;
}
tasklet_init(demo_tasklet, demo_tasklet_handler, 0);
return 0;
}
static void demo_tasklet_exit(void)
{
tasklet_kill(demo_tasklet);
kfree(demo_tasklet);
}
static ssize_t trigger_tasklet_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf,
size_t count)
{
printk("%s, value = %s", __func__, buf);
tasklet_schedule(demo_tasklet); // calling tasklet
return count;
}
static struct kobj_attribute trigger_tasklet_attr = __ATTR(trigger_tasklet, 0220, NULL, trigger_tasklet_store);
static int __init demo_init(void)
{
int ret;
demo_kobj = kobject_create_and_add("demo_tasklet", kernel_kobj);
if (!demo_kobj)
{
return -ENOMEM;
}
ret = sysfs_create_file(demo_kobj, &trigger_tasklet_attr.attr);
if (ret)
{
kobject_put(demo_kobj);
return ret;
}
// init Tasklet
ret = demo_tasklet_init();
if (ret)
{
kobject_put(demo_kobj);
return ret;
}
return 0;
}
static void __exit demo_exit(void)
{
sysfs_remove_file(demo_kobj, &trigger_tasklet_attr.attr);
kobject_put(demo_kobj);
// clear Tasklet
demo_tasklet_exit();
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linx zhang");
MODULE_DESCRIPTION("Demo driver with Tasklet");
11.7 Makefile
obj-m += tasklet.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
11.8 编译
在设备上面, 使用如下命令:
neardi@LPA3588:~/drivers/tasklet$ make
make -C /lib/modules/5.10.110/build M=/home/neardi/drivers/tasklet modules
make[1]: Entering directory '/usr/src/linux-headers-5.10.110'
CC [M] /home/neardi/drivers/tasklet/tasklet.o
MODPOST /home/neardi/drivers/tasklet/Module.symvers
CC [M] /home/neardi/drivers/tasklet/tasklet.mod.o
LD [M] /home/neardi/drivers/tasklet/tasklet.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.10.110'
11.9 测试Tasklet
首先加载驱动, 如下:
neardi@LPA3588:~/drivers/tasklet$ sudo insmod tasklet.ko
neardi@LPA3588:~/drivers/tasklet$ lsmod
Module Size Used by
tasklet 16384 0
其次, 使用sysfs接口来触发调度Tasklet, 如下:
neardi@LPA3588:~/drivers/tasklet$ sudo -i
root@LPA3588:~# echo 1 > /sys/kernel/demo_tasklet/trigger_tasklet
root@LPA3588:~# dmesg
[23056.087153] trigger_tasklet_store, value = 1
[23056.087206] Tasklet handler executed
这样就成功地调用了tasklet处理函数。