您的位置:首页 > 财经 > 金融 > 北京最新的疫情情况_安卓软件开发公司_企业网络营销推广方案策划_福建seo顾问

北京最新的疫情情况_安卓软件开发公司_企业网络营销推广方案策划_福建seo顾问

2025/1/7 8:33:32 来源:https://blog.csdn.net/weixin_63699001/article/details/144229723  浏览:    关键词:北京最新的疫情情况_安卓软件开发公司_企业网络营销推广方案策划_福建seo顾问
北京最新的疫情情况_安卓软件开发公司_企业网络营销推广方案策划_福建seo顾问

文章目录

  • 前言
  • 通用定时器
    • 系统节拍
    • 节拍数与时间转换
    • 基本框架
    • 定时器使用
    • 代码展示
    • 通用定时器特点
  • 高精度定时器

前言

LInux内核定时器是一种基于未来时间点的计时方式,以当前时刻来启动的时间点,以未来的某一时刻为终止点。比如,现在是10点5分,我要定时5分钟,那么定时就是10点5分+5分钟=10点10分。这个和手机闹钟很相似。比如你要定一个第二天早晨8点的闹钟,就是当前时间定时到第二天早晨8点。

通用定时器

系统节拍

在了解定时器之前,先来了解一下内核系统节拍的设置。硬件定时器提供时钟源,时钟源的频率可以设置, 设置好以后 就周期性的产生定时中断,系统使用定时中断来计时。中断周期性产生的频率就是系统频率, 也叫做节拍率(tick rate)(有的资料也叫系统频率),比如 1000Hz,100Hz 等等说的就是系统节拍 率。系统节拍率是可以设置的,单位是 Hz,我们在编译 Linux 内核的时候可以通过图形化界面 设置系统节拍率,按照如下路径打开配置界面:

-> Kernel Features -> Timer frequency ( [=y])

内核系统节拍设置
默认情况下选择 100Hz。设置好以后打开 Linux 内核源码根目录下的.config 文件中CONFIG_HZ会等于设置好的值,Linux 内核会使用 CONFIG_HZ 来设置自己的系统时 钟。打开文件 include/asm-generic/param.h,有如下内容:

# undef HZ 
# define HZ CONFIG_HZ 
# define USER_HZ 100 
# define CLOCKS_PER_SEC (USER_HZ)

这里需要注意的是,并不是节拍率越高越好,高节拍率会提高系统时间精度,但是高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担。

Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数,系统启动的时候会 将 jiffies 初始化为 0,jiffies 定义在文件 include/linux/jiffies.h 中,如下图所示,jiffies_64 和 jiffies 其实是同一个东西,jiffies_64 用于 64 位系统,而 jiffies 用于 32 位系统。
在这里插入图片描述

节拍数与时间转换

为了方便开发,Linux 内核提供了几个 jiffies 和 ms、us、ns 之间的转换函数,如表所示:
在这里插入图片描述
有时候我们需要在内核中实现短延时,尤其是在 Linux 驱动中。Linux 内核提供了毫秒、微 秒和纳秒延时函数:
在这里插入图片描述

基本框架

Linux内核使用timer_list结构体表示内核定时器,timer_list定义在文件include/linux/timer.h中,定义如下:

struct timer_list {/** All fields that change during normal runtime grouped to the* same cacheline*/struct hlist_node   entry;  unsigned long       expires;   /*定时器超时时间,单位节拍数*/void            (*function)(unsigned long);/*定时处理函数*/unsigned long       data;/*要传递function函数的参数*/u32         flags;#ifdef CONFIG_TIMER_STATSint         start_pid;void            *start_site;char            start_comm[16];
#endif
#ifdef CONFIG_LOCKDEPstruct lockdep_map  lockdep_map;
#endif
};

在上面这个结构体中,有几个参数需要重点关注一下。一个是expires到期时间,单位是节拍数。等于定时的当前的始终节拍计数(存储在系统的全局变量和jiffies)+定时时长对应的时钟节拍数量。

那么如何把时间 转换成节拍数量呢?示例:假如从现在开始定时1秒,转换成节拍数量是多少呢? 内核中有一个宏HZ,表示一秒对应的时钟节拍数,那么我们就可以通过这个宏来把时间转换成节拍数。所以,定时1秒就是expires = jiffies + 1*HZ。前面已经说明了HZ是怎么设置的。

定时器使用

  • 当我们定义了一个 timer_list 变量以后一定 要先用 init_timer 初始化一下
void init_timer(struct timer_list *timer)
  • add_timer 函数用于向 Linux 内核注册定时器,使用 add_timer 函数向内核注册定时器以后, 定时器就会开始运行
void add_timer(struct timer_list *timer)
  • del_timer 函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。
int del_timer(struct timer_list * timer)
  • del_timer_sync 函数是 del_timer 函数的同步版,会等待其他处理器使用完定时器再删除,
int del_timer_sync(struct timer_list *timer)
  • mod_timer 函数用于修改定时值,如果定时器还没有激活的话,mod_timer 函数会激活定时器!
int mod_timer(struct timer_list *timer, unsigned long expires)

其中timer:要修改超时时间(定时值)的定时器。 expires:修改后的超时时间。

代码展示

下面这个代码实现了内核定时器周期性的点亮和熄灭开发板上的 LED 灯,当CMD = 3 是LED 灯的闪烁周期可以由内核定时器来设置,这里用到了ioctrl的相关知识,linux内核给用户提供了两类系统调用函数:一类是数据操作函数,比如read、write…。 另外一类函数是非数据操作函数,比如ioctl…,用户程序可以用ioctl给底层设备发送指令。 如果不是很熟悉这一块内容的话可以参考我的下一篇帖子:嵌入式驱动开发详解5(ioctl的使用)
应用层代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "string.h"
#include <stdlib.h>
#include <sys/ioctl.h>#define CLOSE_CMD  			_IO(0XEF, 0X1)   /* 关闭定时器 */
#define OPEN_CMD   			_IO(0XEF, 0X2)   /* 打开定时器 */
#define SETPERIOD_CMD  		_IO(0XEF, 0X3)   /* 设置定时器周期命令 */int main(int argc,char *argv[])
{int fd,ret;char *filename;unsigned int cmd;unsigned int arg;unsigned char str[100];if(argc != 2){printf("Error Usage!!!\r\n");return -1;}filename = argv[1];fd = open(filename ,O_RDWR);if(fd < 0){printf("file %s open failed!\r\n",filename);return -1;}while(1){printf("Input CMD:");ret = scanf("%d",&cmd);if(ret != 1){  /* 参数输入错误 */fgets(str,100,stdin);   /* 防止卡死 */}if(cmd == 1)cmd = CLOSE_CMD;else if(cmd == 2)cmd = OPEN_CMD;else if(cmd == 3){cmd = SETPERIOD_CMD;printf("Input Timer Period:");ret = scanf("%d",&arg);if(ret != 1){fgets(str,100,stdin); }}ioctl(fd,cmd,arg);}ret = close(fd);if(ret < 0){printf("file %s close failed! \r\n",filename);return -1;}return 0;
}

内核层代码如下:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>  //copy
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h> 
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>  //淇″彿閲�  浜掞拷锟戒綋
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define TIMER_CNT 1
#define TIMER_NAME "timer"
#define CLOSE_CMD  			_IO(0XEF, 0X1)   /* 鍏抽棴瀹氭椂鍣� */
#define OPEN_CMD   			_IO(0XEF, 0X2)   /* 鎵撳紑瀹氭椂鍣� */
#define SETPERIOD_CMD  		_IO(0XEF, 0X3)   /* 璁剧疆瀹氭椂鍣ㄥ懆鏈熷懡浠� */   //铏界劧姝ゅ娌℃湁鍐檃rg锛屼絾鏄渶鍚庡鏋滄湁arg鍙傛暟杩樻槸鍙互浼犺繘鏉�#define LED_ON 1
#define LED_OFF 0struct timer_dev{dev_t devid; int major;int minor;struct cdev cdev;struct class *class;struct device *device;struct device_node *nd;int led_gpio;int timeperiod;  			struct timer_list timer;  spinlock_t lock;
};struct timer_dev timerdev;static int led_init(void)
{int ret;timerdev.nd = of_find_node_by_path("/gpioled");if(timerdev.nd == NULL){printk("timerdev node cant not find!!\r\n");ret = -1;goto fail_node;}else{printk("timerdev node found!!\r\n");}timerdev.led_gpio = of_get_named_gpio(timerdev.nd,"led-gpio",0);if(timerdev.led_gpio < 0){printk("cant not get led-gpio\r\n");ret = -1;goto fail_node;}printk("led-gpio-num=%d\r\n",timerdev.led_gpio);gpio_request(timerdev.led_gpio,"led");ret = gpio_direction_output(timerdev.led_gpio,1);if(ret < 0){printk("can`t set gpio!!!\r\n");}return 0;
fail_node:return ret;}static int timer_open(struct inode *inode, struct file *file)
{int ret = 0;file->private_data = &timerdev;timerdev.timeperiod = 1000;ret = led_init();if(ret < 0){return ret;}return 0;
}static long timer_unlocked_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{unsigned long flags;int timerperiod;struct timer_dev *dev = filep->private_data;switch (cmd) {case CLOSE_CMD:del_timer_sync(&dev->timer);break;case OPEN_CMD:spin_lock_irqsave(&dev->lock, flags);timerperiod = dev->timeperiod;spin_unlock_irqrestore(&dev->lock, flags);mod_timer(&dev->timer,jiffies+msecs_to_jiffies(timerperiod));break;case SETPERIOD_CMD:spin_lock_irqsave(&dev->lock, flags);dev->timeperiod = arg;spin_unlock_irqrestore(&dev->lock, flags);mod_timer(&dev->timer,jiffies+msecs_to_jiffies(dev->timeperiod));break;default:break;}return 0;
}void timer_function(unsigned long arg)
{struct timer_dev *dev = (struct timer_dev *)arg;  //姝ゅ闇€瑕佹敞鎰忎紶杩涙潵鐨勬槸鍦板潃static int sta =1;int timerperiod;unsigned long flags;sta = !sta;gpio_set_value(dev->led_gpio,sta);spin_lock_irqsave(&dev->lock, flags);timerperiod = dev->timeperiod;spin_unlock_irqrestore(&dev->lock, flags);mod_timer(&dev->timer,jiffies+msecs_to_jiffies(timerperiod));
}static const struct file_operations timer_fops = {.owner	= THIS_MODULE,.open	= timer_open,.unlocked_ioctl	= timer_unlocked_ioctl,
};static int __init timer_init(void)
{int ret;/* 鍒濆鍖栧師瀛愬彉閲� */spin_lock_init(&timerdev.lock);if(timerdev.major){timerdev.devid = MKDEV(timerdev.major,timerdev.minor);ret = register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME);}else{ret = alloc_chrdev_region(&timerdev.devid,0,TIMER_CNT,TIMER_NAME);timerdev.major = MAJOR(timerdev.devid);timerdev.minor = MINOR(timerdev.devid);printk("alloc_chrdev_region major=%d minor=%d\r\n",timerdev.major, timerdev.minor);}if (ret < 0) {printk("Could not register\r\n");goto fail_devid;}timerdev.cdev.owner = THIS_MODULE;cdev_init(&timerdev.cdev, &timer_fops);ret = cdev_add(&timerdev.cdev,timerdev.devid,TIMER_CNT);if(ret < 0){printk("Could not cdev\r\n");goto fail_cdev;}timerdev.class = class_create(THIS_MODULE,TIMER_NAME);if(IS_ERR(timerdev.class)){ret = PTR_ERR(timerdev.class);goto fail_class;}timerdev.device = device_create(timerdev.class,NULL,timerdev.devid,NULL,TIMER_NAME);if(IS_ERR(timerdev.device)){ret = PTR_ERR(timerdev.device);goto fail_device;}//鍒濆鍖杢imer锛岃缃畾鏃跺櫒澶勭悊鍑芥暟锛岃繕鏈缃懆鏈燂紝鎵€浠ヤ笉浼氭縺娲诲畾鏃跺櫒init_timer(&timerdev.timer);timerdev.timer.function = timer_function;timerdev.timer.data = (unsigned long)&timerdev;  //璁剧疆瑕佷紶閫掔粰 timer_function 鍑芥暟鐨勫弬鏁颁负 timerdev 鐨勫湴鍧€return 0;
fail_device:class_destroy(timerdev.class);
fail_class:cdev_del(&timerdev.cdev);
fail_cdev:unregister_chrdev_region(timerdev.devid,TIMER_CNT);
fail_devid:return ret;
}static void __exit timer_exit(void)
{gpio_set_value(timerdev.led_gpio,1);del_timer_sync(&timerdev.timer);printk("timer_exit\r\n");cdev_del(&timerdev.cdev);unregister_chrdev_region(timerdev.devid,TIMER_CNT);device_destroy(timerdev.class,timerdev.devid);class_destroy(timerdev.class);
}module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");	
MODULE_AUTHOR("hbb");

通用定时器特点

内核定时器定时精度不高,不能作为高精度定时器使用。并且内核定时器并不是周期性运行的,超时以后就会自动关闭,因此如果想要实现周期性定时,那么就需要在定时处理函数中重新开启定时器。

高精度定时器

Linux内核提供了高精度定时器,能够实现纳秒(ns)级别的定时精度。这主要得益于内核中的hrtimer框架,它允许开发者创建和运行高精度的定时器。
关于详细的hrtimer结构体的实现可以参考下面这篇文章linux内核定时器

到这里对通用定时器大致说明就结束了,后面有新的相关的重要的内容会继续进行更新。
对代码有兴趣的同学可以查看链接https://github.com/NUAATRY/imx6ull_dev

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com