精华内容
下载资源
问答
  • Linux 内核timer定时器

    本文参考文档

    头文件以及简单使用流程

    头文件: #include <linux/timer.h>

    简单使用流程:

    1. struct timer_list test_timer;
    2. setup_timer(&test_timer, timer_print, 1234);
    3. test_timer.expires = jiffies + 5 * HZ;
    4. add_timer(&test_timer);

    说明

    1. 定义定时器 test_timer

    值得注意的是,定义定时器时注意定时器为全局变量或者是通过kmalloc从堆中申请到的内存。否则定时器会在函数生命周期结束后被销毁,导致出现错误。
    

    2. setup_timer初始化test_timer

    首先来看一下setup_timer的源代码

    #define setup_timer(timer, fn, data)                    \
        __setup_timer((timer), (fn), (data), 0)
    
    #define __setup_timer(_timer, _fn, _data, _flags)           \
        do {                                \
            __init_timer((_timer), (_flags));           \
            (_timer)->function = (_fn);             \
            (_timer)->data = (_data);               \
        } while (0)

    很容易可以看出,这个函数只是简单的往test_timer这个数据结构中填充一些内容。其实在add_timer这个函数执行之前,test_timer结构里的数据都可以随意更改。其中1234是随手写的一个数据,将会作为参数传入触发器的function中(在这里是timer_print)。其实这个data参数可以通过强转传入指针,例如:
    (unsigned long)&test_timer;

    3. 设定时间

    test_timer.expires = jiffies + 5 * HZ;
    在这句代码中expires成员是定时器,设定了触发函数的时间。HZ代表的是系统每一秒的滴答时钟计算数,可以从config中查到(shell命令,本人内核是3.7.10)。
    #cat /boot/config-3.7.10 |grep CONFIG_HZ
    其中jiffies是LINUX中定义的一个全局变量,记录的是从系统启动到当前使用的时间。假如要计算系统已经运行的时间,可以简单通过以下计算获得:
    jiffies / HZ
    这一句的意思是将定时器设置为5秒后触发(由于程序运行本身需要消耗一些时间,因此并不是绝对精确的。假如在这句语句定义后调用msleep(1000)睡眠3秒,那么可能定时器就会在add_timer之后,只剩下不足4秒的时间了)。

    4. 添加定时器 add_timer(&test_timer);

    Linux内核中定时器的实现涉及到一个双向链表,这个链表中的每一个节点都是
    struct timer_list
    类型。初始化的的简单调用过程,从kernel/init/main.c到kernel/kernel/timer.c
    start_kernel()->init_timers()
    更深一层的内容原理请查看参考文档

    代码

    源代码 test_timer.c

    #include <linux/module.h>
    #include <linux/timer.h>
    
    struct timer_list test_timer;
    
    /*static DEFINE_TIMER(test_timer, test_print, 0, 0)*/
    void timer_print(unsigned long data)
    {
        printk(KERN_INFO "timer_printk %ld\n", data);
        return;
    }
    
    static int __init test_init(void)
    {
        printk(KERN_INFO "test_timer\n");
        setup_timer(&test_timer, timer_print, 900);
        test_timer.expires = jiffies + 5*HZ;
        add_timer(&test_timer);
        return 0;
    }
    
    static void test_exit(void)
    {
        printk(KERN_INFO "test_exit\n");
        del_timer(&test_timer);
        return;
    }
    
    module_init(test_init);
    module_exit(test_exit);
    MODULE_LICENSE("GPL");

    Makefile

    ccflags-y += -O0 -g
    obj-m := test_timer.o
    KDIR :=/lib/modules/$(shell uname -r)/build
    PWD :=$(shell pwd)
    
    default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
    clean:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
    展开全文
  • 内核态延时和定时器

    2019-09-30 23:08:28
    用户:setitimer,getitimer   4、高精度定时器 void hrtimer_init(struct hrtimer *timer, clockid_t which_clock, enum hrtimer_mode mode); timer.function = hr_callback; int ...
    1、jiffies
    HZ=100,jiffies=1 means 10ms。
     
    2,延时
    unsigned long timeout = jiffies + (3*HZ);
    while (hwgroup->busy) {
    if (time_after(jiffies, timeout)) {
     return -EBUSY;
     }
    }
     
    unsigned long timeout = HZ;
    schedule_timeout(timeout); /* Allow other parts of the kernel to run */
     
    短延时:mdelay, udelay, ndelay
     
    3,时间
    clock_settime, clock_gettime
    do_gettimeofday
    time()
    localtime()
    mktime()
    gettimeofday()
     
     
    3,定时器
    #include <linux/timer.h>
    struct timer_list my_timer;
    init_timer(&my_timer); /* Also see setup_timer() */
    my_timer.expire = jiffies + n*HZ; /* n is the timeout in number of seconds */
    my_timer.function = timer_func; /* Function to execute after n seconds */
    my_timer.data = func_parameter; /* Parameter to be passed to timer_func */
    add_timer(&my_timer); /* Start the timer */
    其他函数:mod_timer, del_timer
    用户态:setitimer,getitimer
     
    4、高精度定时器
    void hrtimer_init(struct hrtimer *timer, clockid_t which_clock, enum hrtimer_mode mode);
    timer.function = hr_callback;
    int hrtimer_start(struct hrtimer *timer, ktime_t tim,
     const enum hrtimer_mode mode);
    int hrtimer_cancel(struct hrtimer *timer);
     
     
     
     

    转载于:https://www.cnblogs.com/soul-stone/p/6131072.html

    展开全文
  • 内核定时器

    2014-05-20 19:20:15
    内核定时器 分类: LINUX设备驱动程序第三版 2013-01-21 20:57 475人阅读 评论(0) 收藏 举报 内核定时器 如果我们需要在将来的某个时间点调试执行某个动作,同时在该时间点到达之前不会阻塞当前...
    内核定时器
    分类: LINUX设备驱动程序第三版 475人阅读 评论(0) 收藏 举报

    内核定时器

    如果我们需要在将来的某个时间点调试执行某个动作,同时在该时间点到达之前不会阻塞当前里程,则可以使用内核定时器。内核定时器可以在未来的某个时间点(基于时间滴答)调度执行某个函数。如硬件无法产和中断,则可以周期性的轮询设备状态。

    一个内核定时器是一个数据结构struct timer_list,它告诉内核在用户定义的时间点使用用户定义的参数来执行一个用户定义的函数。其实现位于<linux/timer.h>和kernel/timer.c文件。我们将在内核定时器的实现一节中对此进行详细描述。

    被调度运行的定时器中的函数几乎肯定不会在注册这些函数的进程正在执行的时候运行。相反这些函数会异步的运行。目前为止,我们提供的示例驱动程序代码都在进程执行系统调用的上下文中运行。但是,当定时器运行时,注册该定时器的进程可能正在休眠或在其他处理器上运行,或者干脆已经退出。

    这种异步执行类似于硬件中断发生的情景。实际上,内核定时器常常是作为“软件中断”的结果而运行的。在这种原子性的上下文中运行时,代码会受到许多限制。定时器函数必须以我们在我们在第五章“自旋锁和原子上下文”一节中讨论的方式原子地运行,中间不能休眠,这种非进程上下文还会带来其他一些问题。现在我们就来讨论这些限制。

    许多动作需要在进程上下文中才能执行。如果处于进程上下文之外(比如在中断上下文中),则必须遵守如下规则

    • 不允许访问用户空间。因为没有进程上下文,无法将任何特定进程与用户空间关联起来。
    • current指针在原子模式下是没有任何意义的,也是不可用的,因为相关代码和被中断的进程没有任何联系。
    • 不能执行休眠或调度。原子代码不可以调用schedule或者wait_event,也不能调用任何可能引起休眠的函数。例如,调用kmalloc就不本规则。信号量也不能用,因为可能引起休眠。

    内核代码可以通过in_interrupt()来判断自己是否正运行于中断上下文,该函数无参数,如果是返回非零,无论是硬件中断还是软件中断。

    函数in_atomic()可以判断调度是否被禁止,如果禁止函数返回非零。在硬件和软件中断上下文以及拥有自旋锁的任何时间点都不允许调度。在拥有自旋锁时current是可用的,但禁止访问用户空间,因为这会导致调度的发生。

    不管何时使用in_interrupt()都应该考虑是否真正应该使用的是in_atomic()。这两个函数均在<asm/hardirq.h>中声明。

    内核定时器的另一个重要特性是,任务(定时任务)可以将自己注册以在稍后的时间重新运行。这种可能性是因为每个timer_list结构都会在运行之前从活动定时器链表中移走,这就可以立即链入其它的链表。尽管多次调度同一任务似乎是一你件没多大意义的操作,但有时还是很有用的。如在轮询设备时可以使用,以及在跑I/O的过程中查询速率。

    另一个值得了解的是,在SMP系统中,定时器函数会由注册它的同一CPU执行,这样可以尽可能获得缓存的局域性(locality)。因此,一个注册自己的定时器始终会在同一CPU上运行。

    关于定时器,还有一点要谨记的是:即使在单处理器系统上,定时器也会是竞态的潜在来源。这是由其它异步执行(有可能是软件中断还可能被硬件中断打断)的特点直接导致的。因此,任何通过定时器函数访问的数据结构都应该针对并发访问进行保护。可以使用原子类型或自自旋锁

     

    定时器API

    内核为驱动程序提供了一组用来声明、注册和删除内核定时器的函数。

    void init_timer(struct timer_list *timer);
    struct timer_list TIMER_INITIALIZER(_function, _expires, _data);

    void add_timer(struct timer_list *timer);
    int del_timer(struct timer_list *timer);

    timer_list在使用前必须初始化,在初始化之后,可在调用add_timer之前修改那三个公共的字段(expires,fun,data),如果要在定时器到期前禁止一个已注册的定时器,则可调用del_timer函数。下面是定时器示例代码:

    1. unsigned long j = jiffies;  
    2.   
    3. /*为定时器函数填充数据*/  
    4. data->prevjiffies = j;  
    5. data->buf = buf2;  
    6. data->loops = JIT_ASYNC_LOOPS;  
    7.   
    8. /* register the timer */  
    9. data->timer.data = (unsigned long)data;  
    10. data->timer.function = jit_timer_fn;  
    11. data->timer.expires = j + tdelay;  
    12. add_timer(&data->timer);  
    13.   
    14. /*等待缓冲区以填充数据*/  
    15. wait_event_interruptible(data->wait, !data->loops);  

    实际的定时器函数如下:

    1. void jit_timer_fn(unsigned long arg)  
    2. {  
    3.     struct jit_data *data = (struct jit_data *)arg;  
    4.     unsigned long j = jiffies;  
    5.     data->buf += sprinf(data->buf, "%9li %3li    %i    %6i    %i    %s\n",  
    6.                  j, j - data->prevjiffies, in_interrupt() ? 1 : 0,  
    7.                  current->pid, smp_processor_id(), current->comm);  
    8.       
    9.     if(--data->loops){  
    10.         data->timer.expires += tdelay;  
    11.         data->timer.prevjifies = j;  
    12.         add_timer(&data->timer);  
    13.     }else{  
    14.         wake_up_interruptible(&data->wait);  
    15.     }  
    16. }  

    除了上面的几个函数接口外,内核定时器API还包含其他几个函数,下面给出这些函数的完整描述:

    int mod_timer(struct timer_list *timer, unsigned long expires);

    更新某个定时器到期时间,经常用于超时定时器。我们也可以在通常使用add_timer的时候在不活动的定时器上调用mod_timer。

    int del_timer_sync(struct timer_list *timer)

    和del_timer的工作类似,但该函数可确保在返回时没有任何CUP在运行定时器函数。它可用于SMP系统上避免竞态,这和单处理器内核中的del_timer是一样的。在大多数情况下,应该优先调用这个函数而不是del_timer函数。在非原子上下文中调用,该函数可能会引起休眠,但在其他情况下调用会进入忙等待。在拥有锁时,应该格外小心调用del_timer_sync,因为如果定时器函数企图获取相同的锁,系统就会进入死锁。

    int timer_pending(const struct timer_list *timer);

    该函数通过读取timer_list结构的一个不可见字段来返回定时器是否正在被调度运行。

    内核定时器的实现

    外部机制:当产生时钟中断后,在时钟中断处例程timer_interrupt()中上半部分会调用update_process_times()函数,该函数更新进程的时间片以及修改修改的进程的动态优先级,如果有必要将会告诉高度器重新调试,它还会调用run_local_timers(),在这个函数中标记TIMER_SOFTIRQ,表明有timer需要运行,以便内核在适当的时候去运行定时器,这样softirq机制在适当的时候就会运行定时器队列

    内部机制:

    Linux实现定时器的算法比较精巧,我们不妨先来看看几个简单的算法:

    一个最直接的方法就是将所有的定时器按照时间从小到大的顺序排列起来,这样每次时钟中断下半部分处理例程只要检查当前节点是不是到期就可以了。如果没有到期,那么在下次时钟中断再判断,如果到期了,就执行规定的操作,然后将当前节点的指针往后移一个。在实现上这种方法很简单,也不需要要多余的空间。但是如果链表很长,第次插入的时候排序就要花比较多的时间。

    对于上面的方法,我们可以采用hash表的方式来改进,即采用平均分段的方式来组织链表。比如,我们将到期的jiffies数按照0-99,100-199,200-299分段,每一个定时器到自己所属的时段中进行排序。但是当定时器数量太大的时候,这个方法和上面的那个方法面临同样的问题,那就是在插入的时候花在查找上的时间太大。

    如果我们采用不平均的方法来分段,那么情况就大为不同了。如0-3,4-7,8-15,16-31,……。区间长度呈指数上升,这样,就不会有太多的分段。而且当前要处理的定时器在比较短的链表中,排序和搜索速度都可以大大加快。因为我们关心的都是当前时刻要处理的定时器,而对离执行时间还有很长的定时器是不是需要关心的,所以时间距离太远的定时器我们只要将它连到链表中就可以了,用不着排序。Linux内核就是采用了上面平均分段的hash表思想。

    下面是定时器最基本的一个数据结构:

    struct timer_list{
        struct list_head entry;
        unsigned long expires; //到达该jiffies值时,将调用fun函数,它并不是jiffies_64,因为定时器并不适用于长的未来时间点
        ……
        void (*fun)(unsigned long);
        unsigned long data; //fun函数的参数

        struct timer_base_s *base;//定时器应该在注册它的同一CPU上运行,该字段告诉我们哪个CPU在运行定时器,它指向per-CPU数据结构
    }

    不管何时内核代码注册了一个定时器(通过add_timer或者mod_timer),其操作最终会由internal_add_timer(kernel/timer.c中定义)执行,该函数又会将新的定时器添加到当前CPU关联的“级联表”中的定时器又向链表中。

    关于“级联表”的工作方式简单说一下,内核通过 下面结构管理每个CPU上的定时器:

    struct tvec_base {
     spinlock_t lock;
     struct timer_list *running_timer;
     unsigned long timer_jiffies;
     unsigned long next_timer;
     struct tvec_root tv1;//保存在接下来的0-255个jiffies中到期的定时器,它是256个链表,每个链表指向在同一时刻到期的一组定时器
     struct tvec tv2;//保存在256-256*64-1个jiffies中到期的定时器,它是64个链表,每个链表指向一组定时器
     struct tvec tv3;//保存在256*64-256*64*64-1个jiffies中到期的定时器
     struct tvec tv4;//256*64*64-256*64*64*64-1
     struct tvec tv5;//256*64*64*64-256*64*64*64*64
    }

    通过以上结构就能表达在接下来2^32个jiffies到期的定时器。

    __run_timers被激发时,它会执行当前定时器上的所有挂起的定时器。如果当前jiffies是256的倍数,该函数还会将一下级定时器链表重新散列到256个短期链表中,同时还能根据jiffies的位对其他级别的定时器做级联处理。

    这种方法看起来有些复杂,但能很好处理定时器不多或有大量定时器的情况。用来管理每个活动定时器所需的必要时间和已注册的定时器数量无关,这个时间被限定于定时器expires字段二进制表达上的几个逻辑操作。这种实现唯一的开销在于512个链表头(256个短期链表和4组64个的长期链表)占用了4KB(512*2*4)的存储空间。

    __run_timers运行在原子上下文中,即使我们运行的不是抢占式的内核,定时器也会在正确的时间到期。

    需要谨记的是,内核定时器离完美还有很大的距离,因为它受到jitter以及由硬件中断、其他定时器和异步任务所产生的影响。和简单数字I/O关联的定时器对简单任务来说足够了,比如控制步进电机或者业余电子设备,但通常不适合于工业环境下的生产系统。对这类任务,我们需要借助某种实时的内核扩展。

    展开全文
  • RT-thread内核定时器管理

    千次阅读 2017-05-31 15:47:04
    rt-thread采用软件定时器线程模式或硬件定时器中断模式来实现系统定时器管理。而rt-thread操作系统在默认情况下是采用的硬件定时器中断模式的方式,用户可以通过宏定义RT_USING_TIMER_SOFT来修改定时器管理模式。 ...

    一、前言

    rt-thread采用软件定时器线程模式硬件定时器中断模式来实现系统定时器管理。而rt-thread操作系统在默认情况下是采用的硬件定时器中断模式的方式,用户可以通过宏定义RT_USING_TIMER_SOFT来修改定时器管理模式。

    硬件定时器中断模式是利用MCU芯片本身提供的硬件定时器功能,一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断(比如stm32的嘀嗒定时器中断),在硬件定时器中断服务中检查rt-thread系统定时器是否超时。硬件定时器中断模式的精度一般很高,可以达到纳秒级别,并且是中断触发方式(如滴答定时器中断,其他MCU硬件定时器中断)。

    软件定时器线程模式是指由操作系统提供的一类系统接口,它构建在MCU硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。在该模式中定时器的超时检查在线程入口函数中进行,但定时器的精度仍取决于MCU硬件定时器精度,因为在此模式下系统当前时钟计数rt_tick仍然在MCU硬件定时器中断服务(如stm32的嘀嗒定时器中断)中递增,定时器超时检查时需要比较rt_tick与timeout_tick。

    二、定时器基本工作原理

          无论是软件定时器线程模式,还是硬件定时器中断模式, 在rt-thread定时器模块中都维护两个变量:1、当前系统的时间点rt_tick(当MCU硬件定时器中断时加1)2、定时器链表rt_soft_timer_list(软件定时器线程模式)以及rt_timer_list(硬件定时器中断模式)。在两种模式下,定时器超时检查函数中一旦检查到定时器超时,则先将该定时器从链表中移除,然后执行超时函数后。定时器在创建或初始化时默认为单次定时,若此时定时器内核对象标志设为RT_TIMER_FLAG_PERIODIC,则执行超时函数后会重新启动该定时器即将该定时器重新加入定时器链表中。

          在硬件定时器中断模式下不存在定时器线程,系统中新创建的定时器都会被按照超时时间点timeout_tick从小到大排序的方式插入到rt_timer_list链表中,rt_timer_list的每个节点保留了一个定时器的信息,并且在这个节点加入定时器链表之前就计算好了定时器的超时时间点,即timeout_tick。在MCU硬件定时器中断服务中,除了rt_tick加1以外,还通过定时器超时检查函数rt_timer_check检查定时器链表rt_timer_list中的定时器是否超时,即rt_tick是否赶上timeout_tick,若定时器超时,则调用定时器超时函数。

         在软件定时器线程模式下则存在定时器线程,系统中新创建的定时器都会被按照超时时间点timeout_tick从小到大排序的方式插入到rt_soft_timer_list链表中,rt_soft_timer_list的每个节点保留了一个定时器的信息,并且在这个节点加入定时器链表之前就计算好了定时器的超时时间点,即timeout_tick。在线程入口函数rt_thread_timer_entry中通过不断获取当前rt_tick值,将其与定时器超时时间点timeout_tick对比从而判断定时器是否超时,并进行定时器超时检查函数,一旦发现定时器超时就调用定时器超时函数rt_soft_timer_check,即定时器超时处理函数。

    技术分享

    三、定时器管理控制块:在include/rtdef.h中定义

    /**
     * clock & timer macros
     */
    #define RT_TIMER_FLAG_DEACTIVATED       0x0             /**< 非激活,默认 */
    #define RT_TIMER_FLAG_ACTIVATED         0x1             /**< 激活 */
    #define RT_TIMER_FLAG_ONE_SHOT          0x0             /**< 单次定时,默认 */
    #define RT_TIMER_FLAG_PERIODIC          0x2             /**< 周期性定时*/
    
    #define RT_TIMER_FLAG_HARD_TIMER        0x0             /**< 硬件定时器中断模式,定时器超时检查及超时函数调用在MCU硬件定时器中断服务中执行,默认 */
    #define RT_TIMER_FLAG_SOFT_TIMER        0x4             /**< 软件定时器线程模式,定时器超时检查及超时函数调用在定时器线程入口函数中执行 */
    
    #define RT_TIMER_CTRL_SET_TIME          0x0             /**< set timer control command */
    #define RT_TIMER_CTRL_GET_TIME          0x1             /**< get timer control command */
    #define RT_TIMER_CTRL_SET_ONESHOT       0x2             /**< change timer to one shot */
    #define RT_TIMER_CTRL_SET_PERIODIC      0x3             /**< change timer to periodic */
    
    #ifndef RT_TIMER_SKIP_LIST_LEVEL
    #define RT_TIMER_SKIP_LIST_LEVEL          1
    #endif
    
    /* 1 or 3 */
    #ifndef RT_TIMER_SKIP_LIST_MASK
    #define RT_TIMER_SKIP_LIST_MASK         0x3
    #endif
    
    /**
     * timer structure
     */
    struct rt_timer
    {
        struct rt_object parent;                       //内核对象
    
        rt_list_t        row[RT_TIMER_SKIP_LIST_LEVEL];//链表节点
    
        void (*timeout_func)(void *parameter);        //定时器超时函数
        void            *parameter;                   //定时器超时函数参数 
    
        rt_tick_t        init_tick;                   //定时器定时时间间隔,即每隔多长时间超时  
        rt_tick_t        timeout_tick;                //定时器超时时间点,即超时那一刻的时间点
    };
    typedef struct rt_timer *rt_timer_t;

    四、软件定时器线程模式相关函数:在src/timer.c中

    软件定时器线程初始化:
    void rt_system_timer_thread_init(void);
    在该函数中初始化软件定时器线程模式下定时器链表数组,以及初始化软件定时器线程。
    软件定时器线程入口函数:
    /* system timer thread entry */                 //软件定时器线程入口函数
    static void rt_thread_timer_entry(void *parameter)
    {
        rt_tick_t next_timeout;
    
          while (1)//软件定时器优先级设置最高优先级0,且线程入口函数中为死循环,因此若函数中没有挂起自身线程和执行线程调度,则始终只运行这个线程
        {
            /* get the next timeout tick */         //得到软件定时器线程模式中定时器链表的下一个定时器的超时时间点
            next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list);
            if (next_timeout == RT_TICK_MAX)        //定时器链表为空,即无定时器。RT_TICK_MAX is defined to be 0xffffffff in rtdef.h
            {
                /* no software timer exist, suspend self. */
                rt_thread_suspend(rt_thread_self());//若定时器链表为空,则挂起当前线程,继续线程调度 
                rt_schedule();
            }
            else
            {
                rt_tick_t current_tick;
    
                /* get current tick */
                current_tick = rt_tick_get();       //获取当前时间点  
    
                if ((next_timeout - current_tick) < RT_TICK_MAX/2)//离定时器超时时间点很近了,但是还差一段时间
                {
                    /* get the delta timeout tick */
                    next_timeout = next_timeout - current_tick;   //计算还差多长时间
                    rt_thread_delay(next_timeout);                //休眠一段时间,delay函数将自身线程挂起并启动自身线程定时器后,执行线程调度运行其他就绪线程  
                }
            }
    
            /* check software timer */
            rt_soft_timer_check(); //预计的时间到了,检查是否该产生定时器超时事件。以前的版本中在这里添加了调度器锁(先进入临界区,检查完后再退出临界区)
        }
    }
    定时器超时检查函数:
    void rt_soft_timer_check(void);
    在该函数中扫描定时器链表rt_soft_timer_list中产生超时的定时器,将其移除定时器链表并执行定时器超时函数,若为周期性定时器则重新启动该定时器,即重新将其加入定时器链表中。

    上面代码中,为什么定时器超时检查函数中判断定时器超时的条件是((current_tick - t→timeout_tick) < RT_TICK_MAX/2)?

    因为系统时钟rt_tick溢出后会自动回绕,取定时器比较最大值是定时器最大值的一半,即RT_TICK_MAX/2(在比较两个定时器值时,值是32位无符号数,相减运算将会自动回绕)。

    由此可见,rt-thread系统支持的定时器最长定时时间为RT_TICK_MAX/2,即248天(10ms/tick),124天(5ms/tick),24.5天(1ms/tick)。

    五、硬件定时器中断模式相关函数:在src/timer.c中

    定时器超时检查函数:
    void rt_timer_check(void);
    该函数在MCU硬件定时器中断函数中调用,主要功能为扫描定时器链表rt_timer_list中产生超时的定时器,将其移除定时器链表并执行定时器超时函数,若为周期性定时器则重新启动该定时器,即重新将其加入定时器链表中。
    此函数与rt_soft_timer_check基本大致相同,只不过一个是查找硬件定时器中断模式中定时器链表rt_timer_list,一个是查找软件定时器线程模式中定时器链表rt_soft_timer_list.
    得到下一定时器超时时间点:
    rt_tick_t rt_timer_next_timeout_tick(void)
    {
        return rt_timer_list_next_timeout(rt_timer_list);//得到硬件定时器中断模式中定时器链表的下一个定时器的超时时间点
    }

    六、定时器通用函数接口:在src/timer.c中

    定时器创建:
    rt_timer_t rt_timer_create(const char *name,//定时器名称
                               void (*timeout)(void *parameter),//定时器超时函数
                               void       *parameter,//定时器超时函数参数
                               rt_tick_t   time,//定时器定时时间间隔
                               rt_uint8_t  flag)//定时器内核对象标志
    
    定时器初始化:
    void rt_timer_init(rt_timer_t  timer,//定时器句柄
                       const char *name,//定时器名称
                       void (*timeout)(void *parameter),//定时器超时函数
                       void       *parameter,//定时器超时函数参数
                       rt_tick_t   time,//定时器定时时间间隔
                       rt_uint8_t  flag)//定时器内核对象标志

    #define RT_TIMER_FLAG_DEACTIVATED 0x0 /* 默认为非激活态 */
    #define RT_TIMER_FLAG_ACTIVATED 0x1 /* 激活状态 */
    #define RT_TIMER_FLAG_ONE_SHOT 0x0 /* 默认为单次定时 */
    #define RT_TIMER_FLAG_PERIODIC 0x2 /* 周期定时 */
    #define RT_TIMER_FLAG_HARD_TIMER 0x0 /* 默认为硬件定时器中断模式 */
    #define RT_TIMER_FLAG_SOFT_TIMER 0x4 /* 软件定时器线程模式 */
    定时器删除:
    rt_err_t rt_timer_delete(rt_timer_t timer);
    调用这个函数接口后,系统会把这个定时器从rt_timer_list链表中删除,然后释放相应的定时器控制块占有的内存
    定时器脱离:
    rt_err_t rt_timer_detach(rt_timer_t timer);
    脱离定时器时,系统会把定时器对象从系统容器的定时器链表中删除,但是定时器对象所占有的内存不会被释放。
    定时器启动:
    rt_err_t rt_timer_start(rt_timer_t timer);
    当定时器被创建或者初始化以后,并不会被立即启动,必须在调用启动定时器函数接口后,才开始工作。
    调用定时器启动函数接口后,定时器的状态将更改为激活状态(RT_TIMER_FLAG_ACTIVATED),并按照超时顺序插入到rt_timer_list队列链表中。
    
    定时器停止:
    rt_err_t rt_timer_stop(rt_timer_t timer);
    调用定时器停止函数接口后,定时器状态将更改为停止状态,并从rt_timer_list链表中脱离出来不参与定时器超时检查。当一个(周期性)定时器超时时,也可以调用这个函数接口停止这个(周期性)定时器本身。
    定时器控制:
    rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void *arg);
    #define RT_TIMER_CTRL_SET_TIME 0x0     /* 设置定时器定时时间间隔   */
    #define RT_TIMER_CTRL_GET_TIME 0x1     /* 获得定时器定时时间间隔   */
    #define RT_TIMER_CTRL_SET_ONESHOT 0x2  /* 设置定时器为单一超时型   */
    #define RT_TIMER_CTRL_SET_PERIODIC 0x3 /* 设置定时器为周期型定时器 */


    展开全文
  • 1. 用户态定时器 Linux用户态定时器想必大家都有所了解,以下只列出相关API,如: 1) alarm:超时产生SIGALRM信号的那种; 2) 间隔定时器:getitimer、setitimer; 3) timerfd系列:timerfd_create、timerfd_set...
  • 内核定时器Jiffies

    2021-03-21 20:53:22
    内核定时器Jiffies 简介: 1、头文件 2、定义定时器变量 3、启动函数 4、初始化函数 5、关闭定时器 内核定时器Jiffies 简介: 在驱动中,如果是想指定一个时间去执行某一项操作,可以使用定时器timer,它的...
  • Windows内核定时器总结

    2020-11-18 07:39:29
    总结了两种内核态定时器,IO定时器和DPC定时器。其显著特点是IO定时器精度至少为1秒而DPC定时器可以精确到100ns级别。 DPC定时器使用步骤: KeInitializeTimer初始化定时器 KeInitializeDpc(和1的步骤顺序无所谓...
  • linux 内核定时器

    2019-07-06 11:08:00
    无论何时你需要调度一个动作以后发生, 而不阻塞当前进程直到到时, 内核定时器是给你 的工具. 这些定时器用来调度一个函数在将来一个特定的时间执行, 基于时钟嘀哒, 并且 可用作各类任务; 例如, 当硬件无法发出中断时...
  • Linux内核定时器

    2021-02-07 16:15:02
    经典定时器内核最初版本提供,典型分辨率为4ms,但实际取决于计算机时钟中断运行的频率,也叫做低分辨率定时器或时间轮定时器。 但是对于面向媒体的应用,几毫秒的定时器分辨率不够用。 新的硬件提供了更加精确...
  • 内核通过定时器中断来跟踪时间流,时钟中断由系统定时硬件以周期性的间隔产生,这个时间间隔由内核根据HZ的值设定。如果希望改变系统时钟中断发生的频率,可以修改HZ值,一般保持默认。所以HZ就是指一秒钟内产生多少...
  • Linux内核定时器使用

    2021-01-07 13:39:51
    内核定时器内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于内核源码 linux/timer.h 和 kernel/timer.c 文件中。 被调度的函数肯定是异步执行的,它类似于一种“软件中断”,...
  • 内核定时器是一个数据结构,它告诉内核在用户定义的时间点使用用户定义的参数来执行一个用户定义的函数。其实现位于 &lt;linux/timer.h&gt;中。 内核提供了一组用来声明、注册和删除内核定时器的函数,...
  • 内核定时器的使用

    2019-01-12 09:27:16
    内核定时器的使用  http://blog.chinaunix.net/uid-26611973-id-3415774.html 分类: LINUX 2012-11-21 10:48:14   LINUX内核定时器内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种...
  • android 内核态

    2017-08-22 08:39:24
    今天,简单讲讲android的内核态的知识。 一。内核态(Kernel Mode)与用户态(User Mode)  内核态: CPU可以访问内存所有数据, 包括外围设备, 例如硬盘, 网卡. CPU也可以将自己从一个程序切换到另一个程序 用户...
  • Linux用户态定时器用法以及犯错总结

    千次阅读 2015-09-17 20:39:02
    采样的时候要用到定时器,定时的进行采样。这时候,就会用到setitimer函数了。 1. 要使用setitimer函数,要包含头文件:#include 2. 该函数的原型是:int setitimer(int which, const struct itimerval *new_value,...
  • 内核定时器struct timer_list
  • linux内核--定时器API

    2019-09-28 21:51:10
    /**<linux/timer.h> 定时器结构体 struct timer_list { ........ unsigned long expires; --内核希望定时器执行的jiffies值 void (*function)(unsigned long); --定时器到时时执行的函数 unsigned ...
  • 版权声明:本文为博主原创文章,欢迎转载,转载请注明出处,多谢合作。...采样的时候要用到定时器,定时的进行采样。这时候,就会用到setitimer函数了。 1. 要使用setitimer函数,要包含头文件:#include <sys...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,258
精华内容 4,503
关键字:

内核态定时器