#include <linux/ktime.h>
#include <linux/timekeeping.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
struct my_demo {
struct workqueue_struct *demo_workqueue;
struct delayed_work demo_delay_work;
u32 delay_ms;
};
static struct my_demo *g_demo;
static int g_count;
static unsigned long long int now_ns, prev_ns;
static void delay_work_handle(struct work_struct *work)
{
struct delayed_work *delay_work;
struct my_demo *demo;
now_ns = ktime_get();
printk("%s, interval: %llus,%llums\n", __func__,
(now_ns-prev_ns)/1000000000, ((now_ns-prev_ns)%1000000000)/1000000);
delay_work = to_delayed_work(work);
demo = container_of(delay_work, struct my_demo, demo_delay_work);
queue_delayed_work(demo->demo_workqueue,
&(demo->demo_delay_work),
msecs_to_jiffies(demo->delay_ms));
}
static int __init my_demo_init(void)
{
struct my_demo *demo;
demo = (struct my_demo *)kzalloc(sizeof(*demo), GFP_KERNEL);
if (!demo) {
g_demo = NULL;
return -ENOMEM;
}
g_demo = demo;
demo->demo_workqueue = create_singlethread_workqueue("my_delay_work");
INIT_DELAYED_WORK(&demo->demo_delay_work, delay_work_handle);
g_count = 0;
demo->delay_ms = 2000;
now_ns = ktime_get();
prev_ns = now_ns;
queue_delayed_work(demo->demo_workqueue,
&(demo->demo_delay_work),
msecs_to_jiffies(0));
return 0;
}
static void my_demo_exit(void)
{
bool flag;
// cancel a delayed work and wait for it to finish
// cancel_work_sync(&delayed_work->work) must not be used for delayed_work's
flag = cancel_delayed_work_sync(&(g_demo->demo_delay_work));
if (flag) {
printk("%s, demo_delay_work is pending\n", __func__);
} else {
printk("%s, demo_delay_work is not pending\n", __func__);
}
// Safely terminate a workqueue
// All work currently pending will be done first
destroy_workqueue(g_demo->demo_workqueue);
return;
}
module_init(my_demo_init);
module_exit(my_demo_exit);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("kiss1994");
MODULE_DESCRIPTION ("kernel queue delayed work");
[ 4566.098725] delay_work_handle, interval: 0s,0ms
[ 4568.112612] delay_work_handle, interval: 2s,13ms
[ 4570.128578] delay_work_handle, interval: 4s,29ms
[ 4572.144846] delay_work_handle, interval: 6s,46ms
[ 4574.160830] delay_work_handle, interval: 8s,62ms
[ 4576.176902] delay_work_handle, interval: 10s,78ms
[ 4577.583321] my_demo_exit, demo_delay_work is pending
// 附录:分配连续物理内存并清0
// 对申请的内存大小有限制,不能超过128KB
// static inline void *kzalloc(size_t size, gfp_t flags)
// {
// return kmalloc(size, flags | __GFP_ZERO);
// }
// 附录:queue_delayed_work原型,注意实参
// Equivalent to queue_delayed_work_on() but tries to use the local CPU
// queue work on specific CPU after delay
// Return: %false if @work was already on a queue, %true otherwise.
// If @delay is zero and @dwork is idle, it will be scheduled for immediate execution.
// static inline bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay)
// 附录:cancel_delayed_work_sync原型,注意实参
// cancel a delayed work and wait for it to finish
// Return:
// %true if @dwork was pending, %false otherwise.
// bool cancel_delayed_work_sync(struct delayed_work *dwork)
对同一个 delayed_work 的多次添加
static int __init my_demo_init(void)
{
struct my_demo *demo;
bool flag;
int i;
demo = (struct my_demo *)kzalloc(sizeof(*demo), GFP_KERNEL);
if (!demo) {
g_demo = NULL;
return -ENOMEM;
}
g_demo = demo;
demo->demo_workqueue = create_singlethread_workqueue("my_delay_work");
INIT_DELAYED_WORK(&demo->demo_delay_work, delay_work_handle);
g_count = 0;
demo->delay_ms = 2000;
now_ns = ktime_get();
prev_ns = now_ns;
for (i = 0; i < 4; i++) {
flag = queue_delayed_work(demo->demo_workqueue,
&(demo->demo_delay_work),
msecs_to_jiffies(1000)*5);
if (flag) {
printk("%s, delay_work add workqueue success\n", __func__);
} else {
printk("%s, delay_work already in workqueue\n", __func__);
}
}
return 0;
}
可以看到,第一次添加成功,后续添加都失败
[ 5607.407611] my_demo_init, delay_work add workqueue success
[ 5607.407616] my_demo_init, delay_work already in workqueue
[ 5607.407618] my_demo_init, delay_work already in workqueue
[ 5607.407619] my_demo_init, delay_work already in workqueue
[ 5612.493791] delay_work_handle, interval: 5s,86ms
[ 5614.509734] delay_work_handle, interval: 7s,102ms
[ 5616.525820] delay_work_handle, interval: 9s,118ms
[ 5618.545845] delay_work_handle, interval: 11s,138ms
句柄的执行时长和queue_delayed_work中指定的时长不一致
// 两秒后 demo_delay_work 重新入队 demo_workqueue
// 但是当前的句柄执行时间超过2秒,为5秒
static void delay_work_handle(struct work_struct *work)
{
struct delayed_work *delay_work;
struct my_demo *demo;
bool flag;
now_ns = ktime_get();
printk("%s, interval: %llus,%llums\n", __func__,
(now_ns-prev_ns)/1000000000, ((now_ns-prev_ns)%1000000000)/1000000);
delay_work = to_delayed_work(work);
demo = container_of(delay_work, struct my_demo, demo_delay_work);
flag = queue_delayed_work(demo->demo_workqueue,
&(demo->demo_delay_work),
msecs_to_jiffies(demo->delay_ms));
if (flag) {
printk("%s, delay_work add workqueue success\n", __func__);
} else {
printk("%s, delay_work already in workqueue\n", __func__);
}
mdelay(1000*2);
printk("%s, cur handle is busy first\n", __func__);
mdelay(1000*3);
printk("%s, cur handle is busy second\n", __func__);
}
可以看到,前一个handler不执行完成的话,后一个根本跑不起来,即使 queue_delayed_work 中指定的时间很精确也没有用
[ 6279.363071] my_demo_init, delay_work add workqueue success
[ 6284.492710] delay_work_handle, interval: 5s,129ms
[ 6284.492718] delay_work_handle, delay_work add workqueue success
[ 6286.502742] delay_work_handle, cur handle is busy first
[ 6289.515950] delay_work_handle, cur handle is busy second
[ 6289.516088] delay_work_handle, interval: 10s,153ms
[ 6289.516092] delay_work_handle, delay_work add workqueue success
[ 6291.518687] delay_work_handle, cur handle is busy first
[ 6294.519962] delay_work_handle, cur handle is busy second
[ 6294.520081] delay_work_handle, interval: 15s,157ms
[ 6294.520493] delay_work_handle, delay_work add workqueue success
[ 6296.525910] delay_work_handle, cur handle is busy first
[ 6299.531933] delay_work_handle, cur handle is busy second
[ 6299.532002] delay_work_handle, interval: 20s,168ms
[ 6299.532004] delay_work_handle, delay_work add workqueue success
[ 6301.534915] delay_work_handle, cur handle is busy first
[ 6304.564089] delay_work_handle, cur handle is busy second
[ 6304.565057] delay_work_handle, interval: 25s,202ms
[ 6304.565060] delay_work_handle, delay_work add workqueue success
[ 6306.567208] delay_work_handle, cur handle is busy first
[ 6309.579111] delay_work_handle, cur handle is busy second
... ...