前言
本次实验逻辑依然很简单,count数到5。如果发现代码不能执行,那是不可能的。如果真的不能执行,也让我学习学习。
对于周期性的任务,除了定时器以外,在Linux内核中还可以利用一套封装得很好的快捷机制,其本质是利用工作队列和定时器实现,这套快捷机制就是delayed_work,delayed_work结构体的定义如下所示。
它的成员里有工作队列和定时器。这就是定时器的封装应用啊。为了以后读内核代码的时候,看到这个东西不心虚,决定还是再最后做一个小实验,我坚信大部分人都可以自己使用定时器实现这么一个东西出来。
#include <linux/workqueue.h>
struct delayed_work {
struct work_struct work;
struct timer_list timer;
/* target workqueue and CPU ->timer uses to queue ->work */
struct workqueue_struct *wq;
int cpu;
};
一 struct delayed_work
我们可以通过如下函数调度一个delayed_work在指定的延时后执行:
int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
当指定的delay到来时,delayed_work结构体中的work成员work_func_t类型成员func()会被执行。work_func_t类型定义为:
typedef void (*work_func_t)(struct work_struct *work);
其中,delay参数的单位是jiffies,因此一种常见的用法如下:
schedule_delayed_work(&work, msecs_to_jiffies(poll_interval));
msecs_to_jiffies()用于将毫秒转化为jiffies。如果要周期性地执行任务,通常会在delayed_work的工作函数中再次调用schedule_delayed_work(),周而复始。如下函数用来取消delayed_work:
int cancel_delayed_work(struct delayed_work *work);
int cancel_delayed_work_sync(struct delayed_work *work);
二 相关宏和函数介绍
INIT_DELAYED_WORK
这个宏里做了很多事情,所以不要自己给struct delayed_work变量赋值。
#define INIT_DELAYED_WORK(_work, _func) \
__INIT_DELAYED_WORK(_work, _func, 0)
#define __INIT_DELAYED_WORK(_work, _func, _tflags) \
do { \
INIT_WORK(&(_work)->work, (_func)); \
__setup_timer(&(_work)->timer, delayed_work_timer_fn, \
(unsigned long)(_work), \
(_tflags) | TIMER_IRQSAFE); \
} while (0)
schedule_delayed_work
/**
* schedule_delayed_work - put work task in global workqueue after delay
* @dwork: job to be done
* @delay: number of jiffies to wait or 0 for immediate execution
*
* After waiting for a given time this puts a job in the kernel-global
* workqueue.
*/
static inline bool schedule_delayed_work(struct delayed_work *dwork,
unsigned long delay)
{
return queue_delayed_work(system_wq, dwork, delay);
}
cancel_delayed_work
/**
* cancel_delayed_work - cancel a delayed work
* @dwork: delayed_work to cancel
*
* Kill off a pending delayed_work.
*
* Return: %true if @dwork was pending and canceled; %false if it wasn't
* pending.
*
* Note:
* The work callback function may still be running on return, unless
* it returns %true and the work doesn't re-arm itself. Explicitly flush or
* use cancel_delayed_work_sync() to wait on it.
*
* This function is safe to call from any context including IRQ handler.
*/
bool cancel_delayed_work(struct delayed_work *dwork)
{
unsigned long flags;
int ret;
do {
ret = try_to_grab_pending(&dwork->work, true, &flags);
} while (unlikely(ret == -EAGAIN));
if (unlikely(ret < 0))
return false;
set_work_pool_and_clear_pending(&dwork->work,
get_work_pool_id(&dwork->work));
local_irq_restore(flags);
return ret;
}
EXPORT_SYMBOL(cancel_delayed_work);
cancel_delayed_work_sync
/**
* cancel_delayed_work_sync - cancel a delayed work and wait for it to finish
* @dwork: the delayed work cancel
*
* This is cancel_work_sync() for delayed works.
*
* Return:
* %true if @dwork was pending, %false otherwise.
*/
bool cancel_delayed_work_sync(struct delayed_work *dwork)
{
return __cancel_work_timer(&dwork->work, true);
}
EXPORT_SYMBOL(cancel_delayed_work_sync);
三 测试例程
源码:csi_timer.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#define DEBUG_CT(format,...)\
printk("%s:%s:%d: "format"\n",\
__FILE__,__func__,__LINE__,\
##__VA_ARGS__)
struct ct_dev_{
int count;
struct delayed_work my_delayed_work;
};
struct ct_dev_ *ct_dev;
static void ct_work_func_t(struct work_struct *pwork)
{
struct delayed_work *pd = (struct delayed_work*)
container_of(pwork,struct delayed_work,work);
struct ct_dev_ *p = (struct ct_dev_*)
container_of(pd,struct ct_dev_,my_delayed_work);
DEBUG_CT("p->count = %d",p->count++);
if(p->count < 5){
schedule_delayed_work(&p->my_delayed_work, msecs_to_jiffies(1000));
}
}
static int __init ct_init(void)
{
struct ct_dev_ *p = NULL;
ct_dev = (struct ct_dev_ *)kmalloc(sizeof(struct ct_dev_),GFP_KERNEL);
if(IS_ERR(ct_dev)){
DEBUG_CT("kmalloc error");
return -ENOMEM;
}
p = ct_dev;
DEBUG_CT("");
p->count = 0;
DEBUG_CT("");
INIT_DELAYED_WORK(&p->my_delayed_work, ct_work_func_t);
schedule_delayed_work(&p->my_delayed_work, msecs_to_jiffies(1000));
DEBUG_CT("init ok");
return 0;
}
static void __exit ct_exit(void)
{
struct ct_dev_ *p = ct_dev;
if(IS_ERR(p)){
return;
}
DEBUG_CT("p->count = %d",p->count++);
cancel_delayed_work_sync(&p->my_delayed_work);
kfree(p);
DEBUG_CT("exit ok");
}
module_init(ct_init);
module_exit(ct_exit);
MODULE_LICENSE("GPL");
Makefile
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
KERNELDIR := /home/lkmao/imx/linux/linux-imx
CURRENT_PATH := $(shell pwd)
FILE_NAME=csi_timer
obj-m := $(FILE_NAME).o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
sudo cp $(FILE_NAME).ko /big/nfsroot/jiaocheng_rootfs/home/root/
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
测试结果:
root@hehe:~# insmod csi_timer.ko
[ 22.024402] /big/csi_driver/csi_timer/csi_timer.c:ct_init:41:
[ 22.030262] /big/csi_driver/csi_timer/csi_timer.c:ct_init:43:
[ 22.036256] /big/csi_driver/csi_timer/csi_timer.c:ct_init:47: init ok
root@hehe:~# [ 23.033240] /big/csi_driver/csi_timer/csi_timer.c:ct_work_func_t:26: p->count = 0
[ 24.033238] /big/csi_driver/csi_timer/csi_timer.c:ct_work_func_t:26: p->count = 1
[ 25.033249] /big/csi_driver/csi_timer/csi_timer.c:ct_work_func_t:26: p->count = 2
[ 26.033240] /big/csi_driver/csi_timer/csi_timer.c:ct_work_func_t:26: p->count = 3
[ 27.033235] /big/csi_driver/csi_timer/csi_timer.c:ct_work_func_t:26: p->count = 4