09_Linux内核定时器

这篇具有很好参考价值的文章主要介绍了09_Linux内核定时器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

Linux时间管理和内核定时器简介

内核定时器简介

Linux内核短延时函数

定时器驱动程序编写

编写测试APP

运行测试 


Linux时间管理和内核定时器简介

        学习过UCOS或FreeRTOS的同学应该知道, UCOS或FreeRTOS是需要一个硬件定时器提供系统时钟,一般使用Systick作为系统时钟源。同理,Linux要运行,也是需要一个系统时钟的,至于这个系统时钟是由哪个定时器提供的,在CortexA7内核中有个通用定时器,在《Cortex-A7 Technical ReferenceManua pdf》的“9:Generic Timer”章节有简单的讲解,关于这个通用定时器的详细内容,可以参考《ARM ArchitectureReferenceManual ARMv7-A and ARMv7-R edition.pdf》的"chapter B8 The Generic Timer"章节。这个通用定时器是可选的,按照学习FreeRTOS和STM32的经验,猜测Linux会将这个通用定时器作为Linux系统时钟源(前提是SOC得选配这个通用定时器)。这里仅仅是猜测!不过对于Linux驱动编写者来说,不需要深入研究这些具体的实现,只需要掌握相应的API函数即可,除非你是内核编写者或者内核爱好者。

        Linux内核中有大量的函数需要时间管理,比如周期性的调度程序、延时程序、对于我们驱动编写者来说最常用的定时器。硬件定时器提供时钟源,时钟源的频率可以设置,设置好以后就周期性的产生定时中断,系统使用定时中断来计时。中断周期性产生的频率就是系统频率,也叫做节拍率(tick rate)(有的资料也叫系统频率),比如1000Hz, 100Hz等等说的就是系统节拍率。系统节拍率是可以设置的,单位是Hz,在编译Linux内核的时候可以通过图形化界面设置系统节拍率,按照如下路径打开配置界面:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 选中“Timer frequency”,打开以后如图所示:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 从图可以看出,可选的系统节拍率为100Hz、200Hz、250Hz、300Hz、500Hz和1000Hz,默认情况下选择100Hz。设置好以后打开Linux内核源码根目录下的.config文件,在此文件中有如图所示定义:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 图中的CONFIG_HZ为100, Linux内核会使用CONFIG_HZ来设置自己的系统时钟。打开文件include/asm-generic/param.h,有如下内容:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 第7行定义了一个宏HZ,宏HZ就是CONFIG HZ,因此HZ=100,后面编写Linux驱动的时候会常常用到 HZ,因为HZ 表示一秒的节拍数,也就是频率。

大多数初学者看到系统节拍率默认为100Hz的时候都会有疑问,怎么这么小?100Hz是可选的节拍率里面最小的。为什么不选择大一点的呢?这里就引出了一个问题:高节拍率和低节拍率的优缺点:

1.高节拍率会提高系统时间精度,如果采用100Hz的节拍率,时间精度就是10ms,采用1000Hz的话时间精度就是1ms,精度提高了10倍。高精度时钟的好处有很多,对于那些对时间要求严格的函数来说,能够以更高的精度运行,时间测量也更加准确。

2.高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担,1000Hz和100Hz的系统节拍率相比,系统要花费10倍的“精力”去处理中断。中断服务函数占用处理器的时间增加,但是现在的处理器性能都很强大,所以采用1000Hz的系统节拍率并不会增加太大的负载压力。根据自己的实际情况,选择合适的系统节拍率,这里全部采用默认的100Hz系统节拍率。

Linux内核使用全局变量jiffies来记录系统从启动以来的系统节拍数,系统启动的时候会将 jiffies初始化为0,jiffies定义在文件include/linux/jiffies.h中,定义如下:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 第76行,定义了一个64位的jiffies_64。

第77行,定义了一个unsigned long类型的32位的jiffies。

jiffies_64和jiffies其实是同一个东西,jiffies_64用于64位系统,而jiffies用于32位系统。为了兼容不同的硬件, jiffies其实就是jiffies_64的低32位, jiffies_64和jiffies的结构如图所示:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

当我们访问jiffies的时候其实访问的是jiffies_64的低32位,使用get_jiffies_64这个函数可以获取jiffies_64的值。在32位的系统上读取jiffies的值,在64位的系统上jiffes和jiffies_64表示同一个变量,因此也可以直接读取jiffies的值。所以不管是32位的系统还是64位系统,都可以使用jiffies。

前面说HZ表示每秒的节拍数, jiffies表示系统运行的jiffies节拍数,所以jiffies/HZ就是系统运行时间,单位为秒。不管是32位还是64位的jiffies,都有溢出的风险,溢出以后会重新从0开始计数,相当于绕回来了,因此有些资料也将这个现象也叫做绕回。假如HZ为最大值1000的时候, 32位的jiffies只需要49.7天就发生了绕回,对于64位的jiffies来说大概需要5.8 亿年才能绕回,因此jiffies_64的绕回忽略不计。处理32位jiffies的绕回显得尤为重要,Linux内核提供了如表所示的几个API函数来处理绕回。

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

如果unkown超过known的话, time_after函数返回真,否则返回假。如果unkown没有超过 known的话time_before函数返回真,否则返回假。time_after_eq函数和time_after函数类似,只是多了判断等于这个条件。同理,time_before_eq函数和time_before函数也类似。比如我们要判断某段代码执行时间有没有超时,此时就可以使用如下所示代码:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 timeout就是超时时间点,比如我们要判断代码执行时间是不是超过了2秒,那么超时时间点就是jiffies+(2*HZ),如果jiffies大于timeout那就表示超时了,否则就是没有超时。

第4-6行就是具体的代码段。

第9行通过函数time_before来判断jiffies是否小于timeout,如果小于的话就表示没有超时。

为了方便开发,Linux内核提供了几个jiffies和ms、us、ns之间的转换函数,如表所示:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 文章来源地址https://www.toymoban.com/news/detail-536209.html

内核定时器简介

定时器是一个很常用的功能,需要周期性处理的工作都要用到定时器。Linux内核定时器采用系统时钟来实现,并不是在裸机的PIT等硬件定时器。Linux内核定时器使用很简单,只需要提供超时时间(相当于定时值)和定时处理函数即可,当超时时间到了以后设置的定时处理函数就会执行,和使用硬件定时器的套路一样,只是使用内核定时器不需要做一大堆的寄存器初始化工作。在使用内核定时器的时候要注意一点,内核定时器并不是周期性运行的,超时以后就会自动关闭,因此如果想要实现周期性定时,那么就需要在定时处理函数中重新开启定时器。Linux内核使用timer_list结构体表示内核定时器, timer_list定义在文件include/linux/timer.h中,定义如下(省略掉条件编译):

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 要使用内核定时器首先要先定义一个timer_list变量表示定时器,tiemr_list结构体的expires 成员变量表示超时时间,单位为节拍数。比如现在需要定义一个周期为2秒的定时器,那么这个定时器的超时时间就是jiffies+(2*HZ),因此expires-jiffies+(2*HZ)。function就是定时器超时以后的定时处理函数,要做的工作就放到这个函数里面,需要编写这个定时处理函数。

定义好定时器以后还需要通过一系列的API函数来初始化此定时器,这些函数如下:

init_timer函数

Init_timer函数负责初始化timer_list类型变量,当我们定义了一个timer_list变量以后一定要先用init_timer初始化一下。init_timer函数原型如下:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 timer:要初始化定时器。

返回值:没有返回值。

add_timer函数

add_timer函数用于向Linux内核注册定时器,使用add_timer函数向内核注册定时器以后,定时器就会开始运行,函数原型如下:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 timer:要注册的定时器。

返回值:没有返回值。

del_timer函数

del_timer函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用del_timer函数删除定时器之前要先等待其他处理器的定时处理器函数退出。del_timer函数原型如下:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 timer:要删除的定时器。

返回值: 0,定时器还没被激活; 1,定时器已经激活。

del_timer_sync函数

del_timer_sync函数是del_timer函数的同步版,会等待其他处理器使用完定时器再删除,del_timer sync不能使用在中断上下文中。del_timer_sync函数原型如下所示:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 timer:要删除的定时器。

返回值:0,定时器还没被激活:1,定时器已经激活。

mod_timer函数

mod_timer函数用于修改定时值,如果定时器还没有激活的话, mod_timer函数会激活定时器!函数原型如下:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 timer:要修改超时时间(定时值)的定时器。

expires:修改后的超时时间。

返回值: 0,调用mod_timer函数前定时器未被激活; 1,调用mod_timer函数前定时器已被激活。

 关于内核定时器常用的API函数就讲这些,内核定时器一般的使用流程如下所示:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 

Linux内核短延时函数

有时候我们需要在内核中实现短延时,尤其是在Linux驱动中。Linux内核提供了毫秒、微秒和纳秒延时函数,这三个函数如表所示:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 

定时器驱动程序编写

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

第38~50行,定时器设备结构体,在48行定义了一个定时器成员变量timer。

第60~82行, LED灯初始化函数,从设备树中获取LED灯信息,然后初始化相应的IO

第91-102行,函数timer_open,对应应用程序的open函数,应用程序调用open函数打开/dev/timer驱动文件的时候此函数就会执行。此函数设置文件私有数据为timerdev,并且初始化定时周期默认为1秒,最后调用led_init函数初始化LED所使用的IO

第111~137行,函数 timer_unlocked_ioct,对应应用程序的ioctl函数,应用程序调用ioctl函数向驱动发送控制信息,此函数响应并执行。此函数有三个参数: filp, cmd和arg,其中filp是对应的设备文件, cmd是应用程序发送过来的命令信息, arg是应用程序发送过来的参数,在本章例程中arg参数表示定时周期。

一共有三种命令 CLOSE_CMD, OPEN_CMD和SETPERIOD_CMD,这三个命令分别为关闭定时器、打开定时器、设置定时周期。这三个命令的左右如下:

CLOSE_CMD:关闭定时器命令,调用del_timer_sync函数关闭定时器。

OPEN-CMD:打开定时器命令,调用mod_timer函数打开定时器,定时周期为timerdev的timeperiod成员变量,定时周期默认是1秒。

SETPERIOD-CMD:设置定时器周期命令,参数arg就是新的定时周期,设置timerdev的timeperiod成员变量为arg所表示定时周期指。并且使用mod_timer重新打开定时器,使定时器以新的周期运行。

第140-144行,定时器驱动操作函数集timer_fops

第147~162行,函数timer_function,定时器服务函数,此函有一个参数 arg,在本例程中arg 参数就是timerdev的地址,这样通过arg参数就可以访问到设备结构体。当定时周期到了以后此函数就会被调用。在此函数中将LED灯的状态取反,实现LED灯闪烁的效果。因为内核定时器不是循环的定时器,执行一次以后就结束了,因此在161行又调用了mod_timer函数重新开启定时器。

第169-209行,函数timer_init,驱动入口函数。

第205-207行初始化定时器,设置定时器的定时处理函数为timer_function,另外设置要传递给timer_function函数的参数为timerdev的地址。在此函数中并没有调用timer_add函数来开启定时器,因此定时器默认是关闭的,除非应用程序发送打开命令。

第216-231行,驱动出口函数

219行关闭LED,也就是卸载驱动以后LED处于熄灭状态。

第220行调用del_timer_sync函数删除定时器,也可以使用del_timer函数。

 

编写测试APP

1.运行APP以后提示我们输入要测试的命令,输入1表示关闭定时器、输入2 表示打开定时器,输入3设置定时器周期。

2.如果要设置定时器周期的话,需要让用户输入要设置的周期值,单位为毫秒。

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 第22-24行,命令值。

第53~73行,while(1)循环,让用户输入要测试的命令,然后通过第72行的ioctl函数发送给驱动程序。如果是设置定时器周期命令SETPERIOD_CMD,那么ioctl函数的arg参数就是用户输入的周期值。

运行测试 

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 驱动加载成功以后如下命令来测试:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 输入上述命令以后终端提示输入命令,如图所示:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

输入“2”,打开定时器,此时LED灯就会以默认的1秒周期开始闪烁。在输入"3”来设置定时周期,根据提示输入要设置的周期值,如图所示: 

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 输入“500”,表示设置定时器周期值为500ms,设置好以后LED灯就会以500ms为间隔,开始闪烁。最后可以通过输入“1”来关闭定时器,如果要卸载驱动的话输入如下命令即可:

09_Linux内核定时器,Linux驱动,linux,U-boot,arm开发,嵌入式硬件,设备树

 

到了这里,关于09_Linux内核定时器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Linux 内核定时器

    Linux 内核定时器

    一、相关知识点 (一)知识点 1、内核定时器分类     1)标准定时器或系统定时器     2)高精度定时器(HRT)         头文件:#include linux/hrtimer.h 2、检查系统是否可用HRT     1)查看内核配置文件              2)查看机器         cat proc/timer_list  | grep \\\"resolution\\\" 

    2024年02月11日
    浏览(11)
  • Linux内核 -高精度定时器

    高精度定时器使用示例

    2024年01月19日
    浏览(38)
  • Linux 内核定时器(高级字符设备五)

      在 Linux 内核中很多函数是基于定时器进行驱动的,但是内核定时器的精度并不高,所以不能作为高精度定时器使用。并且内核定时器的运行没有周期性,到达计时终点后会自动关闭。如果要实现周期性定时,就要在定时处理函数中重新开启定时器。   Linux 内核中使用

    2024年02月08日
    浏览(10)
  • 【Orangepi Zero2 全志H616】驱动舵机控制 / Linux定时器(signal、setitimer)

    【Orangepi Zero2 全志H616】驱动舵机控制 / Linux定时器(signal、setitimer)

    一、SG90舵机开发 舵机基本介绍 二、Linux定时器 signal 函数 setitimer 函数原型 signal、setitimer函数API调用 三、舵机 软件PWM实现 如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM信号控制用处: 垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等

    2024年02月05日
    浏览(11)
  • 嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第二天-ARM中断、定时器、看门狗(物联技术666)

    嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第二天-ARM中断、定时器、看门狗(物联技术666)

    链接:https://pan.baidu.com/s/1E4x2TX_9SYhxM9sWfnehMg?pwd=1688 提取码:1688 上午:中断           吕峰老师 下午:定时器 教学内容: 一、中断 ARM 中断分为二级,分为一级中断和二级中断,二级中断为子中断,对于 ARM 来说有 50 个中断源, 其中有 32+ ( EINT23-4 ) 23-4+1-2=50 子中断源

    2024年02月19日
    浏览(14)
  • Linux定时器

    Linux定时器是一种 软件机制 ,用于在指定的时间间隔或特定时间点执行特定的任务。它是 基于内核的机制 ,可以用于各种应用场景,如定时任务调度、延时处理、周期性事件触发等。 运作机制(工作原理):Linux定时器的工作原理主要分为两个部分:定时器的创建和定时器

    2024年02月03日
    浏览(7)
  • 【Linux】Linux定时器使用及代码案例

    在 Linux 中,可以使用定时器来实现一些定时任务,例如定时发送信号、定时执行某个函数等。定时器的初始化通常包括以下几个步骤: 定义定时器结构体:首先需要定义一个定时器结构体,用于存储定时器的相关信息。在 Linux 中,可以使用 timer_t 类型来表示定时器结构体。

    2024年02月04日
    浏览(11)
  • Linux 中的几种定时器

    在linux系统中定时器有分为软定时和硬件定时器。硬件定时器一般指的是CPU的一种底层寄存器,它负责按照固定时间频率产生中断信号,形成信号源。基于硬件提供的信号源,系统就可以按照信号中断来计数,计数在固定频率下对应固定的时间,根据预设的时间参数即可产生

    2024年02月05日
    浏览(11)
  • lv14 内核定时器 11

    lv14 内核定时器 11

    硬件有一个时钟装置,该装置每隔一定时间发出一个时钟中断( 称为一次时钟嘀嗒-tick ),对应的中断处理程序就将 全局变量jiffies_64加1 jiffies_64 是一个全局64位整型, jiffies全局变量为其低32位的全局变量, 程序中一般用jiffies HZ:可配置的宏,表示1秒钟产生的时钟中断次数

    2024年01月22日
    浏览(10)
  • 【Linux 裸机篇(八)】I.MX6U EPIT 定时器中断、定时器按键消抖

    【Linux 裸机篇(八)】I.MX6U EPIT 定时器中断、定时器按键消抖

    EPIT 的全称是: Enhanced Periodic Interrupt Timer,直译过来就是增强的周期中断定时器,它主要是完成周期性中断定时的。学过 STM32 的话应该知道, STM32 里面的定时器还有很多其它的功能,比如输入捕获、 PWM 输出等等。但是 I.MX6U 的 EPIT 定时器只是完成周期性中断定时的,仅此一

    2024年02月02日
    浏览(7)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包