Linux驱动学习笔记:系统节拍和内核定时器

嵌入式技术

1372人已加入

描述

内核会使用CONFIG_HZ来配置自己的系统频率。CONFIG_HZ可以在make menuconfig中配置,配置完的.config文件会有CONFIG_HZ。然后在include/asm-generic/param.h中

# define HZ CONFIG_HZ

一、系统节拍

linux用全局变量jiffies表示系统从开启计算起的节拍数,貌似系统上电后并不会把jiffies初始化成0,而是一个负数。64位的jiffies和32 位的jiffies都存储在.data段,32 位的jiffies 在1000HZ下只需要49.7 天就发生了绕回。绕回的意思是溢出重新计算。因此在32位的系统中需要处理绕回。

API

1.1获取节拍数

使用get_jiffies_64()在32位系统上面获取64位的jiffies,因为如果直接读取的话,读的时候jiffies便发生了变化,因此这个函数是加上了顺序锁保证读取的原子性。

1.2时间对比

1.time_after(a,b)
2.time_before(a,b)
3.time_after_eq(a,b)
4.time_before_eq(a,b)
以上第一个参数都是未知的时间点,第二个参数是已知的时间点,返回值依据名字看。比如time_after
a > b,time_after返回真。
5.time_in_range(a,b,c) //计算a时间点是否在[b,c]这个区间

1.3 时间转换API

API

还有jiffies打交道的两个时间结构体:struct timespec,struct timeval。

struct timespec是秒和纳秒的集合,因此精度高一点。

struct timeval是秒和微秒的集合。

API

jiffies和他们转换的方法:

/*jiffies互转timespec*/
unsigned long timespec_to_jiffies(const struct timespec *value);

jiffies_to_timespec(const unsigned long jiffies,struct timespec *value);

/*jiffies互转timeval*/
timeval_to_jiffies(const struct timeval *value);

jiffies_to_timeval(const unsigned long jiffies,
          struct timeval *value);

二、内核定时器

linux使用timer_list结构体表示内核定时器,定义在include/linux/timer.h中

API

exipires:表示超时时间点,单位是节拍。比如定义一个2s的定时器,超时时间就是jiffies+(2*HZ)

function:时间到了要执行的函数

data:将私有数据传递给定时器回调,因为定时器回调是正在中断上下文执行。

2.1初始化定时器

  1. init_timer(struct timer _list*timer)

API

API

实际上是一个宏定义,要求传入一个指针。只初始化entry→next = NULL和base。

2.TIMER_INITIALIZER

API

API

要求传入定时器回调func,超时时间expires,私有数据data,同时初始化以上字段,但是注意并没有timer实例

3.setup_timer()

API

API

  1. DEFINE_TIMER(_name, _function, _expires, _data)

API

以上可以知道,初始化都比较混乱。因此往后我只使用init_timer+自定义字段,

超时时间设置:expires = jiffes + 需要推后的时间。比如expires = jiffes + HZ,定时一秒。无论如何设置HZ都表示一秒。

2.2启动定时器

API当使用这个函数的时候,定时器便开始计时了。

2.3删除定时器

API

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

API

del_timer_sync函数是 del_timer函数的同步版,只在在多核系统上面才会有区别, 会等待其他处理器运行中的定时器回调运行完再删除定时器, del_timer_sync不能使用在中断上下文。

2.4修改定时值

用于修改定时器的定时值,如果定时器没被激活,就会激活

API

timer:要修改的定时器

expires:修改的超时时间点

当一个内核定时器不会周期定时,需要在回调中重新调用这个函数激活

三、linux短延时函数

API

以上函数本质是忙等待,是会阻塞的。因此不适合在毫秒级别以上的延时使用这些。

四、长延时

API

time_after()、time_before()实际上也是忙等待,只不过等待时间可以很长

用法:

API

不推荐这种用法,长延时不要忙等待。如果需要长延时,请睡眠等待!

五、睡眠等待

msleep

msleep_interruptible

ssleep

等待的时间,是在睡眠CPU可以去执行别的进程,类似于RTOS的延迟

六、使用定时器的例子

#include < linux/init.h >
#include < linux/module.h >
#include < linux/platform_device.h >
#include < linux/kernel.h >
#include < linux/device.h >
#include < linux/cdev.h >
#include < linux/timer.h >
#include < linux/fs.h >
#include < linux/types.h >
#include < linux/jiffies.h >
static struct second_dev{
 dev_t dev_num;
 struct cdev cdev;
 struct device *dev;
 struct class *class;
 struct timer_list second_timer;
 atomic_t cnt;
 struct timeval timval;
}sec_dev;

static int sec_open (struct inode *inode, struct file *filp)
{
 printk("open a kernel timer!\\n");
 
 return 0;
}
static ssize_t sec_read (struct file *filp, char __user *buf, size_t size, \\
    loff_t *ppos)
{
 return 0;
}
static const struct file_operations sec_fops = {
 .owner = THIS_MODULE,
 .open  = sec_open,
 .read = sec_read,
};

static void *second_timer_handler(unsigned long data)
{
 jiffies_to_timespec(jiffies, &sec_dev.timval);
 printk("current jiffies : %d timer : %ld\\n",jiffies,sec_dev.timval.tv_usec); 
 mod_timer(&sec_dev.second_timer, jiffies + msecs_to_jiffies(1));
}
static int second_timer_probe(struct platform_device *pdev)
{
 printk("second_timer_probe jiffies:%d,timer:%ld\\n",jiffies,jiffies/HZ);

 init_timer(&sec_dev.second_timer);
 sec_dev.second_timer.expires = jiffies + HZ;
 sec_dev.second_timer.data = 0;
 sec_dev.second_timer.function = second_timer_handler;
 add_timer(&sec_dev.second_timer);
 alloc_chrdev_region(&sec_dev.dev_num, 0, 1, "sec_timer");
 cdev_init(&sec_dev.cdev, &sec_fops);
 cdev_add(&sec_dev.cdev, sec_dev.dev_num, 1);
 sec_dev.class = class_create(THIS_MODULE, "sec_timer");
 sec_dev.dev = device_create(sec_dev.class, NULL, sec_dev.dev_num, NULL, "sec_timer");
 return 0;
}


static int second_timer_release(struct platform_device *pdev)
{
 device_destroy(sec_dev.class, sec_dev.dev_num);
 class_destroy(sec_dev.class);
 cdev_del(&sec_dev.cdev);
 del_timer(&sec_dev.second_timer);
 return 0;
}

static struct of_device_id second_of_id[] = {
 {.compatible = "second_timer",},
 {}
};

static struct platform_driver second_driver = {
 .driver = {
  .owner = THIS_MODULE,
  .name = "second_timer",
  .of_match_table = second_of_id,
 },
 .id_table = NULL,
 .probe = second_timer_probe,
 .remove = second_timer_release,
};

module_platform_driver(second_driver);
MODULE_LICENSE("GPL");

以上例子需要在设备树定义拥有compatible属性的设备节点。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分