嵌入式技术
内核会使用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位的系统中需要处理绕回。
使用get_jiffies_64()在32位系统上面获取64位的jiffies,因为如果直接读取的话,读的时候jiffies便发生了变化,因此这个函数是加上了顺序锁保证读取的原子性。
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]这个区间
还有jiffies打交道的两个时间结构体:struct timespec,struct timeval。
struct timespec是秒和纳秒的集合,因此精度高一点。
struct timeval是秒和微秒的集合。
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中
exipires:表示超时时间点,单位是节拍。比如定义一个2s的定时器,超时时间就是jiffies+(2*HZ)
function:时间到了要执行的函数
data:将私有数据传递给定时器回调,因为定时器回调是正在中断上下文执行。
实际上是一个宏定义,要求传入一个指针。只初始化entry→next = NULL和base。
2.TIMER_INITIALIZER
要求传入定时器回调func,超时时间expires,私有数据data,同时初始化以上字段,但是注意并没有timer实例
3.setup_timer()
以上可以知道,初始化都比较混乱。因此往后我只使用init_timer+自定义字段,
超时时间设置:expires = jiffes + 需要推后的时间。比如expires = jiffes + HZ,定时一秒。无论如何设置HZ都表示一秒。
当使用这个函数的时候,定时器便开始计时了。
返回值:0定时器还没被激活,1定时器已经激活
del_timer_sync函数是 del_timer函数的同步版,只在在多核系统上面才会有区别, 会等待其他处理器运行中的定时器回调运行完再删除定时器, del_timer_sync不能使用在中断上下文。
用于修改定时器的定时值,如果定时器没被激活,就会激活
timer:要修改的定时器
expires:修改的超时时间点
当一个内核定时器不会周期定时,需要在回调中重新调用这个函数激活
以上函数本质是忙等待,是会阻塞的。因此不适合在毫秒级别以上的延时使用这些。
time_after()、time_before()实际上也是忙等待,只不过等待时间可以很长
用法:
不推荐这种用法,长延时不要忙等待。如果需要长延时,请睡眠等待!
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)
{