一、定时器
1.内核时间相关转换函数
unsigned long usecs_to_jiffies(const unsigned int u)
功能: 把微秒转换成时钟节拍
参数: u 时间微秒
返回: 对应的时钟节拍数量
unsigned long msecs_to_jiffies(const unsigned int m)
功能: 把毫秒转换成时钟节拍
参数: m 时间毫秒
返回: 对应的时钟节拍数量
2.初始化定时器
1)静态初始化(=定义定时器+动态初始化+设置)
DEFINE_TIMER(_name, _function, _expires, _data)
功能: 定义一个名字为_name 的 struct timer_list 结构的变量, 并且初始化它的 function, expires, data 成员。
2)动态初始化
init_timer (struct timer_list *timer);
功能: 只是对 struct timer_list 结构成员进行一些基础初始化操作, function, expires, data 成员还需要用户自己填充。
3)设置定时器
setup_timer(struct timer_list *timer, void *function, unsigned long data);
功能: 设置定时器中的 function, data 和一些基础成员, expires 并没有初始化, 需要用户自己进行初始化
参数:
timer: struct timer_list 结构变量地址
function:超时函数的指针
expires:时间节拍数,可以通过上面的时间相关转换获得
data :超时函数的参数
2.注册定时器到内核
void add_timer(struct timer_list *timer)
功能: 向内核注册一个定时器, 注册后会马上开始计时。
参数: 是一个 struct timer_list 结构变量地址, 并且要求这个结构变量是已经初始化好必须成员
3.修改定时器时间,重新注册
int mod_timer(struct timer_list *timer, unsigned long expires);
功能: 修改定时器定时时间值, 并且重新注册, 不管这个定时的超时函数是否执行过。 执行完成后会马上启动定时。 功能有点像 add_timer。
参数:
timer 要修改的定时器结构变量地址, 要求这个结构变量的 function, data 已经初始化好的。
expires: 有来填充结构中的 expires 成员, 也就是未来的时间点。
4.从内核注销定时器
int del_timer(struct timer_list * timer);
功能: 从内核定时链表上删除指定的定时器, 删除后就不会再执行绑定的函数
参数: 是一个 struct timer_list 结构变量地址
例子:
#include <linux/module.h>
#include <linux/init.h>
//1)添加头文件
#include <linux/timer.h>
//3)定义一个timer_list结构变量
struct timer_list timerlist;
//2)实现一个超时函数
void timerlist_func(unsigned long data)
{
printk("%s is call! data:%d\r\n",__FUNCTION__,(int)data);
///再次修改本定时器超时时间 为当前时间 后面2秒
//mod_timer(&timerlist, jiffies + HZ*2); //循环使用定时器
}
static int __init timerlist_init(void)
{
//4)静态初始化
//DEFINE_TIMER(timelist , time_function, 0, 123);
//4)动态初始化,对timer_list结构变量进行初始
init_timer(&timerlist);
setup_timer(&timerlist, timerlist_func, (unsigned long)123);
timerlist.expires = jiffies + HZ*2;//如果不启用定时器,就不用赋值
//5)注册定时器,启动定时
add_timer(&timerlist); //启动定时器
printk("%s is call!\r\n",__FUNCTION__);
return 0;
}
static void __exit timerlist_exit(void) //Module exit function specified by module_exit()
{
//6)注销定时器
del_timer(&timerlist);
}
module_init(timerlist_init);
module_exit(timerlist_exit);
MODULE_LICENSE("GPL");
二、tasklet机制
1.tasklet定义以及初始化函数
1)静态初始化
DECLARE_TASKLET(name, func, data)
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
作用:定义一个名字为 name 的 tasklet_struct 结构变量,并且初始化这个结构。 所定义的这个 tasklet 是可以被调度,默认是处于被使能状态。
2)静态初始化
DECLARE_TASKLET_DISABLED(name, func, data)
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
作用:定义一个名字为 name 的 tasklet_struct 结构变量,并且初始化这个结构。所定义的这个 tasklet 是不能被调度,默认是处于被禁能状态。 要调度这个 tasklet 需要先使能。
3)动态初始化
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
作用:初始化一个 tasklet_struct 结构变量,初始化的结构默认是处于激活状态,可以被调度。
参数:
t:tasklet核心数据结构,已经申明 好(只需定义),如下:
struct tasklet_struct {
struct tasklet_struct *next; //链表中的下一个tasklet
unsigned long state; //tasklet的状态
atomic_t count; //引用计数器
void(*func) (unsigned long data); //tasklet处理函数 --参数就下面的data
unsigned long data; //给tasklet处理函数的参数
};
**注意:**是一个引用定时器,只有其值为0的时候,tasklet才回被激活;否则被禁止,不能被执行。但是,实际上并没有什么用,因为基本都在激活状态,只是在调度的时候才跑到指针函数那里。
func:是一个指针函数,指向tasklet处理函数,这个处理函数的唯一参数为data
data:func指针函数的参数。
2.tasklet 机制使能、禁能函数
1)使能tasklet
void tasklet_disable(struct tasklet_struct *t)
功能:激活tasklet ,可以调度;实质是就是让count加1。
2)失能tasklet
void tasklet_enable (struct tasklet_struct *t)
功能:禁止tasklet ,不可以调度;实质是就是让count减1。
注意:如果对一个 tasklet_struct 结构变量调用了两次 tasklet_disable 函数,则 count 会增加 1两次,这时你再调用一次tasklet_enable ,那实际 count 值还是 1,还是处于非激活状态。
**3.tasklet 调度函数 **
tasklet_schedule (struct tasklet_struct *t)
功能:tasklet_struct 结构中绑定了一个函数,当用户需要执行这个函数时候,需要自己调用 tasklet_schedule 函数去通知内核帮我们调度所绑定的函数,但是具体什么时候执行就有内核来处理。(要是绑定函数还没有执行,无论调度多少次,效果等同一次。)
4.tasklet取消调度函数
void tasklet_kill(struct tasklet_struct *t)
函数功能:该函数确保指定的tasklet不会被再次调度运行。如果tasklet正被调度执行,该函数会等待其退出。一般来说,设备要被关闭或者模块要被移除时,我们通常调用这个函数。(注意:不能在tasklet的处理函数中调用这个函数,否则会系统异常)
例子:
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/interrupt.h>
#include <linux/delay.h>
#define mode 1 //0:静态初始化 1:动态初始化
static void my_tasklet_function(unsigned long data);
char my_tasklet_data[]="my_tasklet_function was called.";
#if mode
//1.1)动态定义结构体变量,用于动态初始化
struct tasklet_struct my_tasklet;
#else
//1)静态定义并初始化一个tasklet 内核微线程
DECLARE_TASKLET(my_tasklet,my_tasklet_function,(unsigned long)my_tasklet_data);
#endif
//2)定义tasklet工作函数
static void my_tasklet_function(unsigned long data)
{
printk("%s\r\n",(char *)data);
udelay(2000); //延时2ms,而不是休眠
}
static int __init tasklet_module_init(void)
{
printk("mondule insmod\r\n");
#if mode
//1.2)动态初始化
tasklet_init(&my_tasklet, my_tasklet_function,(unsigned long) my_tasklet_data);
#endif
//3)调度tasklet
tasklet_schedule(&my_tasklet);
return 0;
}
static void __exit tasklet_cleanup(void)
{
printk("mondule remove\r\n");
//4)销毁tasklet
tasklet_kill(&my_tasklet);
}
module_init(tasklet_module_init);
module_exit(tasklet_cleanup);
MODULE_LICENSE("GPL");
三、工作队列
1.共享工作队列调度
1)静态初始化
void work_func(struct work_struct * dat)
{
printk(“%p:”,dat);
}
DECLARE_WORK(mywork,work_func)
2)动态初始化
struct work_struct work;
void work_func(struct work_struct * dat)
{
printk(“%p:”,dat);
}
INIT_WORK(&work, work_func);
功能:定义一个工作结构变量,初始化工作结构。
参数:
work:类型如下:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; /* 工作函数指针 */
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
3)调度
int schedule_work(struct work_struct *work)
功能:调度共享工作队列,将工作结构体变量添加入系统的共享工作队列中,添加入队列的工作完成后会自动从队列中删除。
例子:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
//1)添加头文件
#include <linux/workqueue.h>
//2)定义一个工作函数
void mywork_func(struct work_struct *work)
{
printk("befor:%s is call! work:%p\r\n",__FUNCTION__,work);
ssleep(1); //内核休眠
printk("after:%s is call! work:%p\r\n",__FUNCTION__,work);
}
//3)定义一个 work_struct 结构变量,并且进行初始化
DECLARE_WORK(mywork, mywork_func); //定义并且初始化了
static int __init myworkqueue_init(void)
{
printk("%s is call!\r\n",__FUNCTION__);
printk("&mywork:%p\r\n",&mywork); //输出 mywork变量地址
//3)如果动态初始化,定义好全局变量后,在这里初始化,可以参考下面的例子
//4)开始调度
schedule_work(&mywork);
return 0;
}
static void __exit myworkqueue_exit(void)
{
}
module_init(myworkqueue_init);
module_exit(myworkqueue_exit);
MODULE_LICENSE("GPL");
2.自定义工作队列
1)创建工作队列
create_workqueue(name)
作用: 创建一个名字为 name 的工作队列
参数:
name:工作队列的名字,是一个字符串。
返回值:成功:返回工作队列指针,即struct workqueue_struct 的结构体(需自己定义);失败:NULL。
2)往工作队列加入工作节点
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
功能:往 wq 工作队列中加入工作节点,并调度;
参数:
wq:自定义的工作队列结构变量指针
work:要添加的工作节点
3)销毁工作队列
void destroy_workqueue(struct workqueue_struct *wq)
功能: 销毁一个自定义工作队列
参数:
wq:要销毁的工作队列指针
例子:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
//0)添加头文件
#include <linux/workqueue.h>
struct mydata {
int x;
int y;
struct work_struct work;
int z;
};
struct mydata g_var;
//1)定义一个工作队列指针
struct workqueue_struct * mywq;
//2)定义一个工作函数
void mywork_func(struct work_struct *mywork)
{
struct mydata *p = (struct mydata*)container_of(mywork, struct mydata, work);
printk("%s is call! work:%p\r\n", __FUNCTION__, mywork);
//这个使用全局变量来访问x,y,z
printk("g_var.x:%d,g_var.y:%d,g_var.z:%d,\r\n", g_var.x, g_var.y, g_var.z);
//这个使用函数的形式参数来访问x,y,z
printk("p->x:%d,p->y:%d,p->z:%d,\r\n", p->x, p->y, p->z);
}
static int __init myworkqueue_init(void)
{
printk("%s is call!\r\n", __FUNCTION__);
printk("&g_var.work:%p\r\n", &g_var.work); //输出 mywork变量地址
g_var.x = 123;
g_var.y = 456;
g_var.z = 789;
//3) 创建工作队列
mywq = create_workqueue("mywq");
if(mywq == NULL) {
printk("create_workqueue error\r\n");
return -ENOMEM;
}
printk("create_workqueue ok\r\n");
//4)使用动态初始化工作队列
INIT_WORK(&g_var.work, mywork_func);
//5)开始调度
//schedule_work(&g_var.work); //调度共享工作队列
queue_work(mywq, &g_var.work); //调度自定义工作队列
return 0;
}
static void __exit myworkqueue_exit(void)
{
//6) 销毁工作队列
destroy_workqueue(mywq);
}
module_init(myworkqueue_init);
module_exit(myworkqueue_exit);
MODULE_LICENSE("GPL");
3.延时工作队列
注意:
相同点:和上面的内核工作队列一样,可以实现中断的底部代码功能。
不同点:和内核工作队列惟一区别就是延后的时间可控制。
1)延时调度函数
int schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
功能:延时调度工作函数。登记延时工作,一旦登记完成,CPU在适当的时间去执行延时工作的延后处理函数。
参数:
dwork:要调度的延时工作结构指针。
delay:延后调度的时间,单位是时钟节拍。和内核定时器定时时间单位相同,但是不是到期时间,而是定时时间。
例子:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
//0.添加头文件
#include <linux/workqueue.h>
//1)实现一个工作函数
void mywork_func(struct work_struct *mywork)
{
printk("%s is call! work:%p\r\n", __FUNCTION__, mywork);
}
//2)定义一个延时工作
//struct delayed_work mydelayed_work; //动态定义
DECLARE_DELAYED_WORK(mydelayed_work, mywork_func); //静态定义
static int __init myworkqueue_init(void)
{
printk("%s is call!\r\n", __FUNCTION__);
//2)动态初始化延时工作
// INIT_DELAYED_WORK(&mydelayed_work, mywork_func);
//3)一安装模块就开始调度,1秒后开始调度工作函数
schedule_delayed_work(&mydelayed_work, msecs_to_jiffies(1000)); //调度延时共享工作队列
return 0;
}
static void __exit myworkqueue_exit(void)
{
}
module_init(myworkqueue_init);
module_exit(myworkqueue_exit);
MODULE_LICENSE("GPL");