linux kernel时间管理

    1. 内核中的时间节拍

    内核时钟频率在arm中默认是100,即HZ=100,定义在arch/arm/include/asm/param.h,这个值是可以改变的,内核代码尽量使用HZ宏,不要直接使用100。提高频率虽然可以提高时钟精确度,响应速度,但也会因频繁中断导致系统负担增大。在时间转化的时候,系统会强制HZ能被1000整除。

    jiffies是内核全局变量,系统启动是赋值为0,即每秒时钟中断100次,每个节拍间隔为0.1s,jiffies用来记录系统自启动以来的节拍总数。如果要记录自系统启动以后经过时秒数,可以这样转换jiffies/100。

 

    2. jiffies

在linux/jiffies.h中定义

extern unsigned long volatile jiffies;//历史原因的jiffies变量,32bits

extern u64 jiffies_64;//为了防止溢出新定义的jiffies变量,64bits

 

在默认HZ=100的情况下,jiffies在497天后溢出,jiffes_64有生之年是看不到他溢出的。

arch/arm/kernel/vmlinux.lds.S中有

jiffies = jiffies_64;

由于历史的原因保留jiffies这个变量名称,它是取64位的jiffies_64低32位,而在linux内核时间管理部分的代码使用整个64位,这样既可以保持兼容性,亦避免时间溢出。

获取jiffies使用如下函数u64 get_jiffies_64(void),这个函数实际上是return (u64)jiffies;因jiffies有volatile修饰,而jiffies_64没有,所以不要直接访问jiffies_64。

    2.1 jiffies和时间的转换

这个转换尽量使用内核提供的接口,不要手动去转换,即使转换很简单。另外要注意在32位的jiffies乘时,可能会溢出。

jiffies到毫秒、微秒的互相转换:

extern unsigned int jiffies_to_msecs(const unsigned long j);
extern unsigned int jiffies_to_usecs(const unsigned long j);
extern unsigned long msecs_to_jiffies(const unsigned int m);
extern unsigned long usecs_to_jiffies(const unsigned int u);

 

jiffies到struct  timespec、struct timeval互相转换:

 

<span style="font-size:12px;">struct timespec {
	__kernel_time_t	tv_sec;			/* seconds */
	long		tv_nsec;		/* nanoseconds */
};
struct timeval {
	__kernel_time_t		tv_sec;		/* seconds */
	__kernel_suseconds_t	tv_usec;	/* microseconds */
};</span>

extern unsigned long timespec_to_jiffies(const struct timespec *value);
extern void jiffies_to_timespec(const unsigned long jiffies,  struct timespec *value);
extern unsigned long timeval_to_jiffies(const struct timeval *value);
extern void jiffies_to_timeval(const unsigned long jiffies, struct timeval *value);

 

获取本地时间:

extern void do_gettimeofday(struct timeval *tv);

extern void getnstimeofday(struct timespec *tv);

 

    3. 内核定时器

底半部机制就是将工作推后执行,但是没有个量的概念,反正试不在当前执行就可以,而有时候我们需要将一些任务推后XX具体的时间后再执行,这个时候用内核定时器就是理想的解决方法,定时器处理函数在软中断执行。定时器和work_queue一样调用完后自动停止,除非再次激活触发。

相关文件:#include<linux/timer.h>

struct timer_list {
	/*
	 * All fields that change during normal runtime grouped to the
	 * same cacheline
	 */
	struct list_head entry;//定时器列表入口
	unsigned long expires;//超时时间(jiffies为单位)
	struct tvec_base *base;//用户不使用

	void (*function)(unsigned long);//超时后处理函数
	unsigned long data;//传给超时处理函数的参数

	int slack;

#ifdef CONFIG_TIMER_STATS
	int start_pid;
	void *start_site;
	char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};


3.1 定义:

如:static struct timer_list mytimer;

3.2 初始化:

如:init_timer(&mytimer);

初始化了entry,base,slack

3.3 超时处理函数

如:void mytimer_handle(unsigned long arg);

这个arg就是timer_list.data的值

3.4 进一步初始化timer_list

到此为止还有超时时间expires和超时处理函数function还没有赋值。

如:

mytimer.function = mytimer_handle;

mytimer.expires = jiffies+HZ;

mytimer.data = (unsigned long)NULL;

 

也可以静态定义初始化一步搞定,替代上面4步:

#define DEFINE_TIMER(_name, _function, _expires, _data)		\
	struct timer_list _name =				\
		TIMER_INITIALIZER(_function, _expires, _data)

 

3.5 激活

定时器初始化后,激活了才能工作。

如:add_timer(&mytimer);

或者mod_timer(&mytimer, jiffies+HZ);//1s后超时处理,可在定时器超时处理函数中再次调用达到定量周期性处理

/**
 * add_timer - start a timer
 * @timer: the timer to be added
 *
 * The kernel will do a ->function(->data) callback from the
 * timer interrupt at the ->expires point in the future. The
 * current time is 'jiffies'.
 *
 * The timer's ->expires, ->function (and if the handler uses it, ->data)
 * fields must be set prior calling this function.
 *
 * Timers with an ->expires field in the past will be executed in the next
 * timer tick.
 */
void add_timer(struct timer_list *timer)
{
	BUG_ON(timer_pending(timer));
	mod_timer(timer, timer->expires);
}

3.6 停止

定时器到期之前停止定时器,其实是否到期都可以调用,如果调用时未激活返回0,否则返回1。定时器到期后,自动会停止的。

在SMP上,可能定时器中断在其他定时器上运行了,所以删除定时器时要等待其他处理器的定时器处理函数退出后,因此del_timer_sync()函数可能会阻塞,因此在中断上下文不能用这个函数,del_timer可以在中断上下文使用。

int del_timer(struct timer_list *timer);

int del_timer_sync(struct timer_list *timer);

如:del_timer(&mytimer);

 

    4. 内核延时

内核延时有中断底半部,推后一定时间工作的定时器,但是很多时候驱动需要一些硬件上的延时,而且这些延时都很短。

4.1 忙等待

如:下面的延时1s

unsigned long delay = jiffies + HZ;

//OTHER JOB

while(timer_before(jiffies, delay));

4.2 短延时

基本原理是基于忙等待,这个短延时,不是休眠,不像sleep长延时会有调度时间,独占当前的CPU后能立即得到执行,所以尽量不要延迟很长时间,特别是mdelay,。

void ndelay(unsigned long x);//纳秒延时

void udelay(unsigned long x);//微秒延时

void mdelay(unsigned long x);//毫秒延时

4.3 休眠延时(schedule_timeout)

extern signed long schedule_timeout(signed long timeout);//休眠延时
extern signed long schedule_timeout_interruptible(signed long timeout);//可被信号中断唤醒
extern signed long schedule_timeout_killable(signed long timeout);//可被SIGKILL中断唤醒
extern signed long schedule_timeout_uninterruptible(signed long timeout);//不可提前被唤醒

 

void msleep(unsigned int msecs)//通过schedule_timeout_uninterruptible()实现的,还有usleep,ssleep是微秒,秒的休眠

 

如果不要求时间延时很准确,可以使用定时器和休眠延时的方式,如果既要时间短又要时间精确可以使用忙等待或短延时。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值