linux内核中延迟的工作delayed_work

发布时间 2023-08-14 14:14:47作者: burlingame

转载:linux内核中延迟的工作delayed_work_千册的博客-CSDN博客

前言
本次实验逻辑依然很简单,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

 1 /**
 2  * schedule_delayed_work - put work task in global workqueue after delay
 3  * @dwork: job to be done
 4  * @delay: number of jiffies to wait or 0 for immediate execution
 5  *
 6  * After waiting for a given time this puts a job in the kernel-global
 7  * workqueue.
 8  */
 9 static inline bool schedule_delayed_work(struct delayed_work *dwork,
10                      unsigned long delay)
11 {
12     return queue_delayed_work(system_wq, dwork, delay);
13 }

cancel_delayed_work

 1 /**
 2  * cancel_delayed_work - cancel a delayed work
 3  * @dwork: delayed_work to cancel
 4  *
 5  * Kill off a pending delayed_work.
 6  *
 7  * Return: %true if @dwork was pending and canceled; %false if it wasn't
 8  * pending.
 9  *
10  * Note:
11  * The work callback function may still be running on return, unless
12  * it returns %true and the work doesn't re-arm itself.  Explicitly flush or
13  * use cancel_delayed_work_sync() to wait on it.
14  *
15  * This function is safe to call from any context including IRQ handler.
16  */
17 bool cancel_delayed_work(struct delayed_work *dwork)
18 {
19     unsigned long flags;
20     int ret;
21  
22     do {
23         ret = try_to_grab_pending(&dwork->work, true, &flags);
24     } while (unlikely(ret == -EAGAIN));
25  
26     if (unlikely(ret < 0))
27         return false;
28  
29     set_work_pool_and_clear_pending(&dwork->work,
30                     get_work_pool_id(&dwork->work));
31     local_irq_restore(flags);
32     return ret;
33 }
34 EXPORT_SYMBOL(cancel_delayed_work);

cancel_delayed_work_sync 

 1 /**
 2  * cancel_delayed_work_sync - cancel a delayed work and wait for it to finish
 3  * @dwork: the delayed work cancel
 4  *
 5  * This is cancel_work_sync() for delayed works.
 6  *
 7  * Return:
 8  * %true if @dwork was pending, %false otherwise.
 9  */
10 bool cancel_delayed_work_sync(struct delayed_work *dwork)
11 {
12     return __cancel_work_timer(&dwork->work, true);
13 }
14 EXPORT_SYMBOL(cancel_delayed_work_sync);

三 测试例程

源码:csi_timer.c

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/types.h>
 4 #include <linux/kernel.h>
 5 #include <linux/slab.h>
 6 #include <linux/workqueue.h>
 7  
 8 #define DEBUG_CT(format,...)\
 9     printk("%s:%s:%d: "format"\n",\
10     __FILE__,__func__,__LINE__,\
11     ##__VA_ARGS__)
12     
13 struct ct_dev_{
14     int count;
15     struct delayed_work my_delayed_work;
16 };
17 struct ct_dev_ *ct_dev;
18  
19 static void ct_work_func_t(struct work_struct *pwork)
20 {
21     struct delayed_work *pd = (struct delayed_work*)
22         container_of(pwork,struct delayed_work,work);
23  
24     struct ct_dev_ *p = (struct ct_dev_*)
25         container_of(pd,struct ct_dev_,my_delayed_work);
26     DEBUG_CT("p->count = %d",p->count++);
27     if(p->count < 5){
28         schedule_delayed_work(&p->my_delayed_work, msecs_to_jiffies(1000));
29     }
30 }
31  
32 static int __init ct_init(void)
33 {
34     struct ct_dev_ *p = NULL;
35     ct_dev = (struct ct_dev_ *)kmalloc(sizeof(struct ct_dev_),GFP_KERNEL);
36     if(IS_ERR(ct_dev)){
37         DEBUG_CT("kmalloc error");
38         return -ENOMEM;
39     }
40     p = ct_dev;
41     DEBUG_CT("");
42     p->count = 0;
43     DEBUG_CT("");
44     INIT_DELAYED_WORK(&p->my_delayed_work, ct_work_func_t);
45     schedule_delayed_work(&p->my_delayed_work, msecs_to_jiffies(1000));
46     
47     DEBUG_CT("init ok");
48     return 0;
49 }
50 static void __exit ct_exit(void)
51 {
52     struct ct_dev_ *p = ct_dev;
53     if(IS_ERR(p)){
54         return;
55     }
56     DEBUG_CT("p->count = %d",p->count++);
57     cancel_delayed_work_sync(&p->my_delayed_work);
58     kfree(p);
59     DEBUG_CT("exit ok");
60 }
61 module_init(ct_init);
62 module_exit(ct_exit);
63 MODULE_LICENSE("GPL");
64  
65  

Makefile

 1 export ARCH=arm
 2 export CROSS_COMPILE=arm-linux-gnueabihf-
 3  
 4 KERNELDIR := /home/lkmao/imx/linux/linux-imx
 5 CURRENT_PATH := $(shell pwd)
 6 FILE_NAME=csi_timer
 7 obj-m := $(FILE_NAME).o
 8  
 9 build: kernel_modules
10  
11 kernel_modules:
12         $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
13         sudo cp $(FILE_NAME).ko /big/nfsroot/jiaocheng_rootfs/home/root/
14 clean:
15         $(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