美文网首页
Work queue 工作队列 与Timer 定时器

Work queue 工作队列 与Timer 定时器

作者: Nothing_655f | 来源:发表于2020-07-06 19:41 被阅读0次

    一、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));
    }
    

    相关文章

      网友评论

          本文标题:Work queue 工作队列 与Timer 定时器

          本文链接:https://www.haomeiwen.com/subject/parfqktx.html