一、tasklet_init(...)
该函数的主要功能用于初始化tasklet_struct结构体。具体Linux内核源码设计如下:
tasklet 为软中断,考虑到优先级问题,分别占用向量表(softirq_vec)中的 HI_SOFTIRQ 和 TASK_SOFTIRQ 两类软中断。驱动模块可以中断处理上半部分触发 TASKLET_SOFTIRQ。
tasklet 机制特性:
- 跟普通软中断不一样,同一段 tasklet 代码在某个时刻只能在一个 CPU 上面执行,不销售一般 软中断服务函数(在同一时刻可以被多个 CPU 并发执行)。
- 不同的 tasklet 代码同一时刻可以在多个 CPU 上并发执行。
创建 tasklet 对象方式:静态创建和动态创建 (tasklet_init (…))。不管动态还是静态,都需要传递一个函数地址,此函数就是每个 tasklet 自己需要实现的处理函数 func 来改变一个 tasklet 对象状态。
【代码案例】
taskletinit.c
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/module.h>
static struct tasklet_struct tl;
static unsigned long data = 100;
// 自定义中断处理函数
static void irqtasklet_actionfunc(unsigned long data){
printk("调用自定义中断处理函数:irqtasklet_actionfunc(...)函数.\n");
printk("打印输出tasklet对应的state成员:%ld\n",(&tl)->state);
printk("退出自定义中断处理函数:irqtasklet_actionfunc(...)函数.\n");
}
static int __init taskletinit_initfunc(void){
printk("调用内核模块函数:taskletinit_initfunc(...)函数.\n");
printk("打印输出调用tasklet_init()函数之前data的值:%ld\n",tl.data);
if(tl.func==NULL)
printk("tasklet没有初始化.\n");
// 初始化tasklet_struct变量,相当我们要申请一个软中断
tasklet_init(&tl,irqtasklet_actionfunc,data);
if(tl.func==NULL)
printk("taslet没有初始化.\n");
else{
printk("tasklet初始化成功.\n");
printk("打印输出调用tasklet_init()函数after member data的值:%ld\n",tl.data);
}
printk("退出内核模块函数:taskletinit_initfunc(...)函数.\n");
return 0;
}
static void __exit taskletinit_exitfunc(void){
printk("正常退出内核:tasklet_init(...)函数.\n");
}
MODULE_LICENSE("GPL");
module_init(taskletinit_initfunc);
module_exit(taskletinit_exitfunc);
Makefile
#!/bin/bash
ccflags_y += -O2
ifneq ($(KERNELRELEASE),)
obj-m := taskletinit.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf *.o *.ko *.mod.c
depend .depend dep:
$(CC) -M *.c > .depend
编译插入以及卸载
二、tasklet_schedule(...)
该函数主要功能用于将参数t代表的软中断添加到向量tasklet_vec的尾部,并且触发一个软中断。具体Linux内核源码设计如下:
tasklet_vec
是一个 存储 tasklet
的数据结构,本质是一个 per-CPU 的 tasklet_head
数组:
- 组织管理
tasklet
:每个 CPU 对应一个tasklet_head
,维护该 CPU 上待执行的tasklet
链表。驱动通过__tasklet_schedule
把tasklet
加入tasklet_vec
中,内核后续会遍历该结构执行tasklet
处理函数。 - 配合软中断调度:作为软中断机制的一部分,
tasklet_vec
存储的tasklet
会在软中断触发时被统一处理,实现高效的中断下半部(耗时操作)异步执行,避免阻塞硬中断。
通过此 tasklet_schedule () 函数修改中断状态值,设置 state 字段的值为 1,证明此中断被调用,将它加入中断等待队列当中。然后调用函数 __tasklet_schedule 将中断加入 tasklet_vec 中断向量链表的尾部,等待获取 CPU 资源并被调度处理。
【代码案例】
taskletschedule.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
static struct tasklet_struct tl;
static unsigned long data = 0;
// 自定义软中断处理函数
static void irqtasklet_actionfunc(unsigned long data){
printk("调用自定义中断处理函数:irqtasklet_actionfunc(...)函数.\n");
printk("data: %ld\n",data);
printk("打印输出自定义中断处理函数tasklet的state的值:%ld\n",(&tl)->state); // 显示中断状态
printk("退出自定义中断处理函数:irqtasklet_actionfunc(...)函数.\n");
}
static int __init tasklet_schedule_initfunc(void){
printk("调用内核模块初始化函数:tasklet_schedule_initfunc(...)函数.\n");
// 初始化tasklet
tasklet_init(&tl,irqtasklet_actionfunc,data);
printk("打印输出调用tasklet_init(...)函数之后的tasklet成员state的值为:%ld\n",(&tl)->state);// 显示中断状态
// 将中断变量存放到软中断执行队列当中
tasklet_schedule(&tl);
printk("打印输出调用tasklet_schedule(...)函数之后的state成员的值为:%ld\n",(&tl)->state); // 显示中断状态
printk("退出内核模块初始化函数:tasklet_schedule_initfunc(...)函数.\n");
return 0;
}
static void __exit tasklet_schedule_exitfunc(void){
printk("正常退出内核:tasklet_schedule(...)函数.\n");
}
MODULE_LICENSE("GPL");
module_init(tasklet_schedule_initfunc);
module_exit(tasklet_schedule_exitfunc);
Makefile
#!/bin/bash
ccflags_y += -O2
ifneq ($(KERNELRELEASE),)
obj-m := taskletschedule.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf *.o *.ko *.mod.c
depend .depend dep:
$(CC) -M *.c > .depend
编译插入卸载