內(nèi)核會(huì)使用CONFIG_HZ來配置自己的系統(tǒng)頻率。CONFIG_HZ可以在make menuconfig中配置,配置完的.config文件會(huì)有CONFIG_HZ。然后在include/asm-generic/param.h中
# define HZ CONFIG_HZ
一、系統(tǒng)節(jié)拍
linux用全局變量jiffies表示系統(tǒng)從開啟計(jì)算起的節(jié)拍數(shù),貌似系統(tǒng)上電后并不會(huì)把jiffies初始化成0,而是一個(gè)負(fù)數(shù)。64位的jiffies和32 位的jiffies都存儲(chǔ)在.data段,32 位的jiffies 在1000HZ下只需要49.7 天就發(fā)生了繞回。繞回的意思是溢出重新計(jì)算。因此在32位的系統(tǒng)中需要處理繞回。
1.1獲取節(jié)拍數(shù)
使用get_jiffies_64()在32位系統(tǒng)上面獲取64位的jiffies,因?yàn)槿绻苯幼x取的話,讀的時(shí)候jiffies便發(fā)生了變化,因此這個(gè)函數(shù)是加上了順序鎖保證讀取的原子性。
1.2時(shí)間對(duì)比
1.time_after(a,b)
2.time_before(a,b)
3.time_after_eq(a,b)
4.time_before_eq(a,b)
以上第一個(gè)參數(shù)都是未知的時(shí)間點(diǎn),第二個(gè)參數(shù)是已知的時(shí)間點(diǎn),返回值依據(jù)名字看。比如time_after
a > b,time_after返回真。
5.time_in_range(a,b,c) //計(jì)算a時(shí)間點(diǎn)是否在[b,c]這個(gè)區(qū)間
1.3 時(shí)間轉(zhuǎn)換API
還有jiffies打交道的兩個(gè)時(shí)間結(jié)構(gòu)體:struct timespec,struct timeval。
struct timespec是秒和納秒的集合,因此精度高一點(diǎn)。
struct timeval是秒和微秒的集合。
jiffies和他們轉(zhuǎn)換的方法:
/*jiffies互轉(zhuǎn)timespec*/
unsigned long timespec_to_jiffies(const struct timespec *value);
jiffies_to_timespec(const unsigned long jiffies,struct timespec *value);
/*jiffies互轉(zhuǎn)timeval*/
timeval_to_jiffies(const struct timeval *value);
jiffies_to_timeval(const unsigned long jiffies,
struct timeval *value);
二、內(nèi)核定時(shí)器
linux使用timer_list結(jié)構(gòu)體表示內(nèi)核定時(shí)器,定義在include/linux/timer.h中
exipires:表示超時(shí)時(shí)間點(diǎn),單位是節(jié)拍。比如定義一個(gè)2s的定時(shí)器,超時(shí)時(shí)間就是jiffies+(2*HZ)
function:時(shí)間到了要執(zhí)行的函數(shù)
data:將私有數(shù)據(jù)傳遞給定時(shí)器回調(diào),因?yàn)槎〞r(shí)器回調(diào)是正在中斷上下文執(zhí)行。
2.1初始化定時(shí)器
- init_timer(struct timer _list*timer)
實(shí)際上是一個(gè)宏定義,要求傳入一個(gè)指針。只初始化entry→next = NULL和base。
2.TIMER_INITIALIZER
要求傳入定時(shí)器回調(diào)func,超時(shí)時(shí)間expires,私有數(shù)據(jù)data,同時(shí)初始化以上字段,但是注意并沒有timer實(shí)例
3.setup_timer()
- DEFINE_TIMER(_name, _function, _expires, _data)
以上可以知道,初始化都比較混亂。因此往后我只使用init_timer+自定義字段,
超時(shí)時(shí)間設(shè)置:expires = jiffes + 需要推后的時(shí)間。比如expires = jiffes + HZ,定時(shí)一秒。無論如何設(shè)置HZ都表示一秒。
2.2啟動(dòng)定時(shí)器
當(dāng)使用這個(gè)函數(shù)的時(shí)候,定時(shí)器便開始計(jì)時(shí)了。
2.3刪除定時(shí)器
返回值:0定時(shí)器還沒被激活,1定時(shí)器已經(jīng)激活
del_timer_sync函數(shù)是 del_timer函數(shù)的同步版,只在在多核系統(tǒng)上面才會(huì)有區(qū)別, 會(huì)等待其他處理器運(yùn)行中的定時(shí)器回調(diào)運(yùn)行完再刪除定時(shí)器, del_timer_sync不能使用在中斷上下文。
2.4修改定時(shí)值
用于修改定時(shí)器的定時(shí)值,如果定時(shí)器沒被激活,就會(huì)激活
timer:要修改的定時(shí)器
expires:修改的超時(shí)時(shí)間點(diǎn)
當(dāng)一個(gè)內(nèi)核定時(shí)器不會(huì)周期定時(shí),需要在回調(diào)中重新調(diào)用這個(gè)函數(shù)激活
三、linux短延時(shí)函數(shù)
以上函數(shù)本質(zhì)是忙等待,是會(huì)阻塞的。因此不適合在毫秒級(jí)別以上的延時(shí)使用這些。
四、長延時(shí)
time_after()、time_before()實(shí)際上也是忙等待,只不過等待時(shí)間可以很長
用法:
不推薦這種用法,長延時(shí)不要忙等待。如果需要長延時(shí),請(qǐng)睡眠等待!
五、睡眠等待
msleep
msleep_interruptible
ssleep
等待的時(shí)間,是在睡眠CPU可以去執(zhí)行別的進(jìn)程,類似于RTOS的延遲
六、使用定時(shí)器的例子
#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)