Linux time system
Linux中跟时间有关的函数变量有很多,但是对开发者来说无非就是用了计时/延时/挂起/获取时间等,下面列出自己所使用过的一些时间接口,供大家理解使用。
1.jiffies
在内核代码中,到处充斥着jiffies变量,jiffies在include/linux/jiffies.h中定义,在linux启动的时候会将jiffies清0,当系统完成对时钟中断的初始化后,在每个时钟滴答时jiffies都会被加1,所以在驱动程序开发过程中,使用jiffies变量就已足够所有基于jieffies的时间度量任务。
jiffies的使用通常要配合着宏HZ,宏HZ的定义位于.confg,一般都将其设置为1000,1秒1000次即1ms。
例:
time1 = jiffies + 2 * HZ;则time1表示未来的2秒;
time2 = jiffies + 3 * HZ/1000;则time2表示未来的3毫秒;
在jiffies.h还为我们提供了很多接口函数,主要有两类,一类为时间的比较,一类为时间的转化。 下面进行简单说明举例:
1、时间的比较
time_after(a,b); //如果时间点a在时间点b之后,则返回1
time_before(a,b); //如果时间点a在时间点b之前,则返回1
time_after_eq(a,b); //如果时间点a在时间点b之后或等于b,则返回1
time_before_eq(a,b); //如果时间点a在时间点b之前或等于b,则返回1
time_in_range(a,b,c); //如果时间点a在时间点b和c之间或等于b或c,则返回1
例:
int time_test()
{
unsigned long timeout = jiffies + 2 * HZ/1000; //2ms
do_time_task();
if(time_after(jiffies,timeout)) //如果do_time_task()函数执行超过2ms,则成立
return 1;
return 0;
}
2.时间转化
为了方便理解,我们需要将jiffies转化成比较直观的毫秒ms或者是微妙us等形式,jiffies.h里面一个了一些比较直观的转化函数,其实现函数在kernel/time.c里面 如:
unsigned int jiffies_to_msecs(const unsigned long j);
unsigned int jiffies_to_usecs(const unsigned long j);
unsigned long msecs_to_jiffie(const unsigned int m);
unsigned long usecs_to_jiffie(const unsigned int u);
2.do_gettimeofday
当我们需要得到某一段代码所执行的时间是多少,可以使用do_gettimeofday函数,该函数定义在kernel/time/timekeeping.c中,申明在include/linux/time.h中,do_gettimeofday函数是在内核的函数,如果在应用层实现该这个函数则使用gettimeofday,包含sys/time.h即可,使用方法与do_gettimeofday一样。
举个简单的例子:
int gettimeofday_test()
{
struct timeval tv_start;
struct timeval tv_end;
float timeuse;
gettimeofday(&tv_start, NULL);
do_gettimeofday_task();
gettimeofday(&tv_end, NULL);
timeuse = 1000000*(tv_end.tv_sec - tv_start.tv_sec) + tv_end.tv_usec - tv_start.tv_usec;
timeuse /= 1000;
printf("Time-consuming: %f ms\n", timeuse);
return 0;
}
3.delay
delay函数为死等待,这种函数只能用在短延时上,如微妙甚至纳秒等级的 Linux内核提供了如下短延时接口函数,位于include/linux/delay.h中
void mdelay(usigned long msecs);
void udelay(usigned long usecs);
void ndelay(usigned long nsecs);
4.sleep
上面的短延时为忙等待,当为长延时不能为忙等待,需要将任务挂起,这是我们可以使用sleep函数 在kernel/timer.c里面有实现了msleep()函数,也在include/linux/delay.h中申明,其原型如下,
sleep也是我们在写shell脚本时比较经常使用的延时、循环执行命令,直接调用即可使用。
5.内核定时器timer
如果希望在内核中以一定的时间间隔来执行执行某项任务,如:每隔一秒查询一次button的状态,可以使用内核定时器来实现。内核中已经为我们提供了定时器数据结构timer_list,和定时器的接口函数,位于include/linux/timer.h中。
定时器timer的实现步骤就两步:
- 初始化定时器,设置定时器的触发时间,指定该定时器的回调函数。
- 实现定时器回调函数,如果需要循环执行,则需要在该定时器的回调函数中重新启动该定时器。
举个简单的例子:
#define TIMER_FUNC(_fn) void _fn(unsigned long timer_arg)
#define TIMER_INIT(_osdev, _timer, _fn, _arg) \
do { \
init_timer(_timer); \
(_timer)->function = (_fn); \
(_timer)->data = (unsigned long)(_arg); \
} while (0)
#define TIMER_SET(_timer, _ms) mod_timer(_timer, jiffies + ((_ms)*HZ)/1000)
struct timer_list os_timer_test;
static TIMER_FUNC(timer_test_func)
{
do_timer_func();
TIMER_SET(&os_timer_test, 1000); //重新设置该定时器
}
int timer_test()
{
TIMER_INIT(NULL, &os_timer_test, timer_test_func, &os_timer_test); //初始化定时器,绑定回调函数
TIMER_SET(&os_timer_test, 1000); //1s
return 0;
}
6.系统时间date
date主要用来显示和设定系统的日期与时间,通常会在shell中使用到data命令
1.data的显示
最直接的方式即输入date,如下:
# date
Thu Dec 15 14:28:19 GMT 2016
这种方式会将信息以一定的格式显示出来,星期、月份、日期、时:分:秒、时区、年份
如果我们需要得到更具体的信息可以通过"data +%$"来获取,如:
# date +%H //小时
14
# date +%m //月份
12
# date +%y //年份
16
具体指令如下:
命令 | 含义 |
---|---|
%H | 小时(00..23) |
%I | 小时(01..12) |
%k | 小时(0..23) |
%l | 小时(1..12) |
%M | 分钟(00..59) |
%p | 显示本地 AM 或 PM |
%r | 直接显示时间 (12 小时制,格式为 h : m : s [AP]M) |
%s | 从 1970 年 1 月 1 日 00:00:00 UTC 到目前为止的秒数 |
%S | 秒(00..61) |
%T | 直接显示时间 (24 小时制) |
%X | 相当于 %H:%M:%S |
%Z | 显示时区 |
%a | 星期几 (Sun..Sat) |
%A | 星期几 (Sunday..Saturday) |
%b | 月份 (Jan..Dec) |
%B | 月份 (January..December) |
%c | 直接显示日期与时间 |
%d | 日 (01..31) |
%D | 直接显示日期 (mm/dd/yy) |
%h | 同 %b |
%j | 一年中的第几天 (001..366) |
%m | 月份 (01..12) |
%U | 一年中的第几周 (00..53) (以 Sunday 为一周的第一天的情形) |
%w | 一周中的第几天 (0..6) |
%W | 一年中的第几周 (00..53) (以 Monday 为一周的第一天的情形) |
%x | 直接显示日期 (mm/dd/yy) |
%y | 年份的最后两位数字 (00.99) |
%Y | 完整年份 (0000..9999) |
2.data的设置
既然读取到了时钟发现错误时就需要修改,修改的方式有很多,这边就不想弄的那么复杂,用一个最直观简单的格式,如下:
# date -s "2016-12-15 14:43:33"
Thu Dec 15 14:43:33 GMT 2016
7.RTC时钟hwclock
linux系统时钟有两个,一个是硬件时钟,一般就是RTC时钟,另一个是系统时钟,即上面使用date时得到的信息,它是linux系统Kernel时间。当Linux启动时,系统Kernel会去读取硬件时钟的设置,然后系统时钟就会独立于硬件运作。
硬件时钟可以通过hwclock来获取,如下:
# hwclock
Thu Dec 15 11:12:07 2016 0.000000 seconds
当我们修改了系统时钟后,想将系统时间同步到硬件时钟可以通过hwclock -w命令实现,如下:
# date -s "2016-12-15 15:40:00"
Thu Dec 15 15:40:00 GMT 2016
# hwclock
Thu Dec 15 11:24:25 2016 0.000000 seconds
# hwclock -w
# hwclock
Thu Dec 15 15:40:23 2016 0.000000 seconds
busybox里面也实现从硬件时钟同步到系统时钟的命令hwclock -s,但这个指令一般很少用,因为kernel一启动系统时钟就是读取硬件的时钟,所以两则已经相等了,我们发现系统时钟不对时,则会去修改系统时钟再同步到硬件时钟。
Linux time system的分析就到这边,有感悟时会持续会更新。
注:以上内容都是本人在学习过程积累的一些心得,难免会有参考到其他文章的一些知识,如有侵权,请及时通知我,我将及时删除或标注内容出处,如有错误之处也请指出,进行探讨学习。文章只是起一个引导作用,详细的数据解析内容还请查看Linux相关教程,感谢您的查阅。