• Linux驱动中断与时间篇——低分辨率定时器


    在这里插入图片描述

    内核延时函数接口


    延时的函数有delaysleep两种类型:

    delay接口

    void ndelay(unsigned long nsecs);//纳秒延时
    void udelay(unsigned long usecs);//微妙延时
    void mdelay(unsigned long msecs);//毫秒延时
    
    • 1
    • 2
    • 3

    sleep接口

    void msleep(unsigned int msecs);//毫秒级延时
    long msleep_interruptible(unsigned int msecs);//毫秒级延时,可被信号打断
    void ssleep(unsigned int seconds);//秒级延时
    
    • 1
    • 2
    • 3

    delay和sleep的区别

    delay型延时:忙等待,占用CPU资源,延迟过程无法进行其他任务。

    sleep型延时:休眠,不占用CPU资源,其它模块此时可以使用CPU资源。

    低分辨率定时器


    jiffies和HZ

    jiffies全局变量,表示系统启动以来产生的节拍数。每产生一次中断,jiffies自动加一。

    HZ:赫兹,也叫节拍率,表示每秒种产生多少次中断

    例如:HZ200,代表每秒产生200次中断,那2秒钟jiffies的值就应该是400。因此系统的运行时间可以用jiffies/HZ表示。

    一秒钟:jiffies + HZ表示一秒钟

    原因:内核中统计时间是通过jiffies,因此要比较时间或者定时也是通过jiffies。

    例如程序运行一秒钟,内核如何知道运行了一秒?答案是运行一秒后的jiffies值和运行前的jiffies值进行比较,如果相差为一个HZ,则代表一秒钟。jiffies+HZ其实就是一秒后jiffies的值,所以jiffies+HZ可以间接表示一秒钟。

    定时2秒:jiffies + 2*HZ。以此类推

    获取当前的jiffies值,可以用get_jiffies_64()函数。

    将时间转为对应的jiffies值,可以用msecs_to_jiffies()等函数,例如msecs_to_jiffies(1000)代表1秒,函数返回值其实就是HZ

    相关接口

    #include
    
    struct timer_list { 
        struct list_head list;  
        unsigned long expires; 	//定时器到期时间,传入的是jiffies值
        unsigned long data; 	//作为参数被传入定时器处理函数
        void (*function)(unsigned long);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    void init_timer(struct timer_list * timer);//初始化定时器
    void add_timer(struct timer_list * timer);//添加一个定时器
    int mod_timer(struct timer_list *timer, unsigned long expires);//修改定时器的定时时间expires
    int del_timer(struct timer_list * timer);//删除定时器
    
    • 1
    • 2
    • 3
    • 4
    unsigned int jiffies_to_msecs (const unsigned long j);//将jiffies转为对应的毫秒值
    unsigned int jiffies_to_usecs (const unsigned long j);//将jiffies转为对应的微秒值
    unsigned long msecs_to_jiffies (const unsigned int m);//将毫秒值转为对应的jiffies
    unsigned long usecs_to_jiffies (const unsigned int u);//将微秒值转为对应的jiffies
    
    • 1
    • 2
    • 3
    • 4

    定时器使用示例

    使用步骤:

    1、调用init_timer初始化一个定时器,给struct timer_list各成员赋值。

    2、调用add_timer将定时器添加到内核定时器链表,时间到后回调函数自动调用,用mod_timer修改expires的值可实现循环定时。

    3、不需要定时器时,调用del_timer删除。

    单次定时

    加载驱动一秒钟后,打印出“timer handler, data:520”:

    #include 
    #include 
    #include 
    
    #include //jiffies在此头文件中定义
    #include //struct timer_list
    
    struct timer_list timer;
    
    static void timer_handler (unsigned long arg)
    {
    	printk("timer handler, data:%d\n", arg);
    }
    
    static int __init my_init(void)
    {
    	printk("%s enter\n", __func__);
    
    	init_timer(&timer);
    	timer.expires = get_jiffies_64() + msecs_to_jiffies(1000);//定时1秒
    	timer.function = timer_handler;
    	timer.data = 520;
    	add_timer(&timer);
    
        return 0;
    }
    
    static void __exit my_exit(void)
    {
    	printk("%s enter\n", __func__);
    	del_timer(&timer);
    }
    
    module_init(my_init);
    module_exit(my_exit);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    循环定时

    实现循环定时就是在定时时间到了之后,调用mod_timer函数再次修改定时时间

    每隔一秒钟打印“timer handler, data:520

    #include 
    #include 
    #include 
    
    #include //jiffies在此头文件中定义
    #include //struct timer_list
    
    struct timer_list timer;
    
    static void timer_handler (unsigned long arg)
    {
    	printk("timer handler, data:%d\n", arg);
    
    	mod_timer(&timer, get_jiffies_64() + msecs_to_jiffies (1000));
    }
    
    static int __init my_init(void)
    {
    	init_timer(&timer);
    	timer.expires = get_jiffies_64() + msecs_to_jiffies (1000);//定时1秒
    	timer.function = timer_handler;
    	timer.data = 520;
    	add_timer(&timer);
    
        return 0;
    }
    
    static void __exit my_exit(void)
    {
    	del_timer(&timer);
    }
    
    module_init(my_init);
    module_exit(my_exit);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
  • 相关阅读:
    YOLO v7 + 各种跟踪器(SORT, DeepSORT, ByteTrack, BoT-SORT)实现多目标跟踪
    【FAQ】安防视频监控平台EasyNVR无法控制云台,该如何解决?
    JDK8使用Optional避免NullPointerException
    数据结构与算法——栈和队列
    ArcGIS 10.8软件安装包下载及安装教程
    基础到高级涵盖11个技术,Alibaba最新出品711页Java面试神册真香
    Spring源码解析—— AOP代理的生成
    Java程序员该如何进阶?资深阿里P8通过十年经验送你一些经验和建议!
    成都链安CEO杨霞:打通区块链生态安全信息屏障,守护区块链生态安全
    jvm摘要
  • 原文地址:https://blog.csdn.net/qq_32276547/article/details/132920837