Linux内核时钟配置
Linux内核可选的配置系统频率
默认情况下选择的是100HZ。
对应.config配置文件的内容:
CONFIG_HZ_100=yCONFIG_HZ=100
对应到内核源码include/arm-generic中的配置:
#ifndef __ASM_GENERIC_PARAM_H#define __ASM_GENERIC_PARAM_H#include<uapi/asm-generic/param.h># undef HZ# define HZ CONFIG_HZ /* Internal kernel timer frequency */# define USER_HZ 100 /* some user interfaces are */# define CLOCKS_PER_SEC (USER_HZ) /* in "ticks" like times() */#endif/* __ASM_GENERIC_PARAM_H */
宏HZ的值是一秒节拍数(频率)
Linux内核使用jiffies全局变量来记录系统节拍数。
include/linux/jiffies.h
extern u64 __cacheline_aligned_in_smp jiffies_64;externunsignedlongvolatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;
Linux中处理jiffies溢出的函数
time_after(unkown, known) //如果ununkown超过known返回真,否则返回假time_before(unkown, known) //如果ununkown没有超过known返回真,否则返回假time_after_eq(unkown, known) time_before_eq(unkown, known) //unkown 通常为 jiffies//known 通常是需要对比的值。
超时判断示例代码:
unsignedlong timeout;timeout = jiffies + (3 * HZ); //超时时间点,即3s后超时//超时判断if (time_after(jiffies, timeout)) //超时{ ... //超时}else{ ...}
Linux内核提供的jiffies和ms、us、ns之间的转换函数
intjiffies_to_msecs(constunsignedlong j)intjiffies_to_usecs(constunsignedlong j)u64 jiffies_to_nsecs(constunsignedlong j)longmsecs_to_jiffies(constunsignedint m)longusecs_to_jiffies(constunsignedint u)unsignedlongnsecs_to_jiffies(u64 n)
Linux内核定时器
include/linux/timer.h
structtimer_list {/* * All fields that change during normal runtime grouped to the * same cacheline */structhlist_nodeentry;unsignedlong expires;void (*function)(struct timer_list *); u32 flags;#ifdef CONFIG_LOCKDEPstructlockdep_maplockdep_map;#endif}
定时器超时后就会停止,需要周期定时就要在超时函数中重新启动定时器
定时器API
//void init_timer(struct timer_list *timer) //初始化定时器//timer 要初始化的定时器#define __TIMER_INITIALIZER(_function, _flags) { \ .entry = { .next = TIMER_ENTRY_STATIC }, \ .function = (_function), \ .flags = (_flags), \ __TIMER_LOCKDEP_MAP_INITIALIZER( \ __FILE__ ":" __stringify(__LINE__)) \ }//定义一个名为_name,超时回调函数为_function的定时器,并初始化#define DEFINE_TIMER(_name, _function) \ struct timer_list _name = \ __TIMER_INITIALIZER(_function, 0)
voidadd_timer(struct timer_list *timer)//向内核注册定时器,注册后就开始运行
intdel_timer(struct timer_list * timer)//删除定时器intdel_timer_sync(struct timer_list *timer)//返回0表示定时器没有被启动,返回1表示定时器已经被启动。
intmod_timer(struct timer_list *timer, unsignedlong expires)//修改超时时间,如果定时器没有启动,修改后启动定时器//返回0表示修改expires前已经被启动没有启动,1表示expires修改前已经被启动
定时器一般使用模板
structtimer_listtimer;//定时定时器//定时超时回调函数voidtimeout_callback(unsignedlong arg){/* 超时处理代码 *///周期运行需要调用mod_timer重新启动定时器 mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000)); }voidtimer_init(void){ init_timer(&timer); /* 初始化定时器 */ timer.function = timeout_callback; /* 设置定时处理函数 */ timer.expires = jffies + msecs_to_jiffies(2000); /* 设置超时时间为 2 秒 */ timer.data = (unsignedlong)&dev; /* 将设备结构体作为参数 */ add_timer(&timer); /* 将内核注册启动定时器 * / }
voidtimer_deinit(void){ del_timer(&timer);}
Linux内核延时
Linux提供的短延时函数
voidndelay(unsignedlong nsecs)voidudelay(unsignedlong usecs)voidmdelay(unsignedlong mseces)//三个函数都是忙等待延时函数,进程不会进入休眠
长延时
//忙等待长延时/* 延迟 100 个 jiffies */unsignedlong delay = jiffies + 100;while(time_before(jiffies, delay));/* 再延迟 2s */unsignedlong delay = jiffies + 2*Hz;while(time_before(jiffies, delay));
进程休眠延时
//将当前进程添加到等待队列中,从而在等待队列上睡眠 interruptible_sleep_on_timeout可以被中断打断sleep_on_timeout(wait_queue_head_t *q, unsignedlong timeout);interruptible_sleep_on_timeout(wait_queue_head_t*q, unsignedlong timeout);
//此二者本质是调用上面的sleep_on_timeout和interruptible_sleep_on_timeout实现的voidmsleep(unsignedint msecs)//毫秒延时unsignedlongmsleep_interruptible(unsignedint msecs)//毫秒延时,可以被中断打断