一、schedule_work 工作队列
是系统延时调度的一个自定义函数, 一般用来处理中断中底半等耗时操作
1、定义struct work_struct irq_queue;
2、初始化INIT_WORK(&irq_queue,do_irq_queuework);
3、调用方法:schedule_work(&irq_queue);
调用完毕后系统会释放此函数,所以如果想再次执行的话,就再次调用schedule_work()即可。
在使用上和 tasklet最大的不同是工作队列的函数可以使用休眠,而tasklet的函数是不允许使用休眠的。
工作队列的使用又分两种情况,一种是利用系统共享的工作队列来添加自己的工作,这种情况处理函数不能消耗太多时间,这样会影响共享队列中其他任务的处理;另外一种是创建自己的工作队列并添加工作。
(一)利用系统共享的工作队列添加工作:
第一步:声明或编写一个工作处理函数
void my_queue_func();
第二步:创建一个工作结构体变量,并将处理函数和参数的入口地址赋给这个工作结构体变量
//编译时创建名为my_queue_work的结构体变量并把函数入口地址和参数地址赋给它;
DECLARE_WORK(my_queue_work,my_queue_func,&data);
如果不想要在编译时就用DECLARE_WORK()创建并初始化工作结构体变量,也可以在程序运行时再用INIT_WORK()创建
//1. 创建一个名为my_queue_work的结构体变量,创建后才能使用INIT_WORK()
struct work_struct my_queue_work;
//2. 初始化已经创建的my_queue_work
// 其实就是往这个结构体变量中添加处理函数的入口地址和data的地址,通常在驱动的open函数中完成
INIT_WORK(&my_queue_work,my_queue_func,&data);
第三步:将工作结构体变量添加入系统的共享工作队列
schedule_work(&my_queue_work); //添加入队列的工作完成后会自动从队列中删除
或使用延时的方法
schedule_delayed_work(&my_queue_work,tick); //延时tick个滴答后再提交工作
(二)创建自己的工作队列来添加工作
第一步:声明指向工作队列的指针
struct workqueue_struct *p_queue;
p_queue=create_workqueue("my_queue"); //创建一个名为my_queue的工作队列并把工作队列的入口地址赋给声明的指针
第二步:创建工作结构体变量和声明工作处理函数(通常在open函数中完成)
void my_queue_func();
struct work_struct my_queue_work;
INIT_WORK(&my_queue_work, my_queue_func, &data); //创建一个工作结构体变量并初始化,和第一种情况的方法一样
第三步:将工作添加入自己创建的工作队列等待执行
queue_work(p_queue, &my_queue_work);
//作用与schedule_work()类似,不同的是将工作添加入p_queue指针指向的工作队列而不是系统共享的工作队列
第四步:删除自己的工作队列
destroy_workqueue(p_queue); //一般是在close函数中删除
二、Timer 定时器
1、相关API
a. init_timer(struct timer_list):定时器初始化函数;
b. add_timer(struct timer_list):往系统添加定时器;
c. mod_timer(struct timer_list *, unsigned long jiffier_timerout):
修改定时器的超时时间为jiffies_timerout;
(Linux系统中的jiffies是定义在内核里面的一个全局变量,
只是它的单位并不是秒或是毫秒。
通常是250个jiffies为一秒,在内核里面可以直接使用宏定义:HZ
)
d. timer_pending(struct timer_list ):定时器状态查询,
如果在系统的定时器列表中则返回1,否则返回0;
e. del_timer(struct timer_list):删除定时器。
2.相关API函数源码
a)init_timer函数
路径: kernel-3.18/include/linux/timer.h)
#define init_timer(timer) \
__init_timer((timer), 0)
#define __init_timer(_timer, _flags)\
init_timer_key((_timer), (_flags), NULL, NULL)
可以看出 实际上是调用init_timer_key()函数去初始化
(路径: kernel/time/timer.c)
/*
init_timer_key -初始化一个计时器
@timer:要初始化的计时器。
@flags:定时器的旗帜
@name:计时器名称
@key:用于跟踪计时器 同步锁的依赖关系
*注意: 必须先调用init_timer_key()进行初始化,
然后才能调用其他跟定时器相关的方法,例如add_timer,mod_timer等
*/
void init_timer_key(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
debug_init(timer);
do_init_timer(timer, flags, name, key);
}
b)add_timer函数
(路径: kernel/time/timer.c)
/*
add_timer -启动一个计时器,或者说激活一个定时器。
add_timer用于往系统中添加一个定时器,参数timer为要添加的定时器(timer_list)
,当系统时间经过timer->expires这么多时间,就会去调用timer->function回调方法去完成相应的任务。
注意:必须先初始化timer->expires,timer->function,timer->data这三个成员变量,才能调用add_timer()这个方法
@timer:要添加的定时器
*/
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));//打印相关log
mod_timer(timer, timer->expires);//调用mod_timer设置时间
}
c)mod_timer函数
(路径: kernel/time/timer.c)
/*
mod_timer -修改一个timer(定时器)的超时时间
@timer:要修改的计时器。
@expires:新的超时,单位jiffies
分析:mod_timer()是更新活动计时器过期字段(即expires参数)的一种更有效的方法(如果计时器没有被激活,mod_timer会先激活计时器,然后在重新设定超时时间)
*/
int mod_timer(struct timer_list *timer, unsigned long expires)
{
expires = apply_slack(timer, expires);
if (timer_pending(timer) && timer->expires == expires)
return 1;
return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}
实际上 调用mod_timer(timer, expires)相当于
del_timer(timer); timer->expires = expires; add_timer(timer);
注意:如果有多个未序列化的并发用户使用相同的计时器,则mod_timer()是修改超时的唯一安全方法,因为add_timer()不能修改已经运行的计时器。
该函数返回是否已经修改了一个待定定时器
如果调用mod_timer去修改一个定时器,
如果当前定时器处于非激活状态,则该函数返回0,
如果当前定时器处于激活状态,则该函数返回1
Ps:调用了add_timer(),就表示该定时器处于激活状态
d)timer_pending函数
(路径: include/linux/timer.h)
/*
@timer:给定的定时器
timer_pending会告诉给定的计时器是否正在等待
返回值:如果计时器挂起,则为1;如果不是,则为0
如果timer->entry.next为NULL,表示计时器没有挂起,返回0
如果timer->entry.next不等于NULL,表示计时器挂起,返回1
*/
static inline int timer_pending(const struct timer_list * timer)
{
return timer->entry.next != NULL;
}
timer_pending——是否有一个计时器正在等待?
e)del_timer函数
(路径: kernel/time/timer.c)
/*
del_timer -删除(停用)计时器。
*@timer:被停用的计时器
分析:del_timer()禁用计时器——这对激活的和非激活的计时器都有效。
函数返回是否已经禁用了一个待定定时器。
(即。一个非激活计时器的del_timer()返回0,激活计时器返回1)
*/
int del_timer(struct timer_list *timer)
{
struct tvec_base *base;
unsigned long flags;
int ret = 0;
debug_assert_init(timer);
timer_stats_timer_clear_start_info(timer);
if (timer_pending(timer)) {
base = lock_timer_base(timer, &flags);
ret = detach_if_pending(timer, base, true);
spin_unlock_irqrestore(&base->lock, flags);
}
return ret;
}
3.使用定时器的一般流程
1)定义timer_list,、初始化timer_list、编写function;
2)为timer的expires、data、function赋值;
3)调用add_timer将timer往系统添加定时器;(或者直接调用mod_timer方法)
4)在定时器到期时,function被运行;
5)在程序中涉及timer控制的地方适当地调用del_timer、mod_timer删除timer或改动timer的expires。
4. Demo:
/*定义计时器*/
static struct timer_list test_timer;
/*回调函数*/
static void test_timer_callback(unsigned long a)
{
/*这里添加相应的逻辑*/
printk("hello word\n");
/*一般使用场景是调用前面的workqueue 来实现自己的功能逻辑
* schedule_work(&my_queue_work);
*/
/* 如果这里要使用定时器循环的话可以在这里继续调用 timer
* 这样子一旦有地方调用就会开启time 循环了
* mod_timer(&test_timer, jiffies + (10 * HZ));
*/
}
/*主函数*/
void test_timer_init()
{
/*初始化相关参数*/
init_timer(&test_timer);//先初始化timer
test_timer.expires = jiffies + (20 * HZ);//设置超时 20*HZ 表示20秒
test_timer.function = &test_timer_callback;//设置回调函数
test_timer.data = ((unsigned long)0);//设置data参数,回调函数的执行的参数,不需要使用传0 即可
add_timer(&test_timer);//把定时器添加到系统中,激活定时器
/*这里调用mod_timer 通过add timer 一个执行时间为10s的*/
mod_timer(&test_timer, jiffies + (10 * HZ));
}