Linux's Heartbeat
系统的心跳无疑就是“时钟”,心跳稳定,系统才稳定,有心脏病的系统可要不得。
app最常见使用的用法:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/time.h>
4 #include<signal.h>
5
6 int func(void)
7 {
8 printf("hello timer\n");
9 alarm(2);
10 return 0;
11 }
12
13
14 int main(void)
15 {
16 signal(SIGALRM, func);
17 alarm(2);
18 while(1)
19 ;
20 return 0;
21 }
app更精确的定时方式:
1 #include<stdio.h>
2 #include<signal.h>
3 #include<sys/time.h>
4
5 int limit = 10;
6
7 int timeout_info(int signo)
8 {
9 if (limit == 0)
10 {
11 printf("time limit reached!\n");
12 return 0;
13 }
14
15 printf("only %d sec left...\n", limit--);
16
17 return 0;
18 }
19
20
21
22 int init_sigaction(void)
23 {
24 struct sigaction act;
25
26 /*
27 * struct sigaction {
28 * __sighandler_t sa_handler;
29 * unsigned long sa_flags;
30 * __sigrestore_t sa_restorer;
31 * sigset_t sa_mask;
32 * };
33 */
34
35
36 /*初始化各项*/
37 act.sa_handler = timeout_info;
38 act.sa_flags = 0;
39 sigemptyset(&act.sa_mask);
40
41 //sigaction(SIGPREAL, &act, NULL);
42 sigaction(SIGVTALRM, &act, NULL);
43 //sigaction(SIGPROF, &act, NULL);
44
45 return 0;
46 }
47
48
49
50
51
52 int init_timer(void)
53 {
54 struct itimerval val;
55
56
57 /*
58 * struct itimerval
59 * {
60 * struct timeval it_interval; //时间间隔
61 * struct timeval it_value; //当前时间计数
62 * };
63 */
64
65
66 /*距火箭发射(定时器工作)还有几秒*/
67 val.it_value.tv_sec = 2;
68 val.it_value.tv_usec = 0;
69
70
71
72 /*火箭发射!(定时器工作)*/
73 val.it_interval.tv_sec = 1;
74 val.it_interval.tv_usec = 0;
75
76
77 /*linux内置的三个定时器的细微差别*/
78 //setitimer(ITIMER_REAL, &val, NULL); //SIGALRM信号
79 setitimer(ITIMER_VIRTUAL, &val, NULL); //SIGPVTPARLM信号
80 //setitimer(ITIMER_PROF, &val, NULL); //SIGPROF信号
81
82 /*定时发送或者采用 (while+sleep)+sigqueue 方式*/
83
84
85 return 0;
86 }
87
88
89
90 int main(void)
91 {
92 init_sigaction(); //设置一个定时信号接收器
93 init_timer(); //设置一个定时器发送定时信号
94
95 printf("10 sec...\n");
96
97 while(1)
98 ;
99
100 return 0;
101 }
再来个简单的内核定时测试程序:
1 #include <linux/module.h>
2 #include <linux/init.h>
3 #include <linux/delay.h>
4 #include <linux/timer.h>
5
6 struct timer_list my_timer;
7
8 void timer_func(unsigned long args)
9 {
10 printk("boom [%d]\n", args);
11
12 /* 定时任务执行时可以顺便设置下一次定时
13 * my_timer.expires = jiffies + 2*HZ;
14 * add_timer(&my_timer);
15 */
16 }
17 int init_test(void)
18 {
19 my_timer.expires = jiffies + 5*HZ;
20 my_timer.function = timer_func;
21 my_timer.data = 99;
22
23 init_timer(&my_timer); //初始化定时器
24
25 printk("start\n");
26 add_timer(&my_timer); //激活定时器,也就是加入上述相应的定时链表中
27
28 return 0;
29 }
30
31
32 void exit_test(void)
33 {
34 del_timer(&my_timer);
35 printk("bye\n");
36 }
37
38
39 module_init(init_test);
40 module_exit(exit_test);
41
42 MODULE_LICENSE("GPL");
43
44 MODULE_AUTHOR("Jesse");
45 MODULE_DESCRIPTION("this is test module");
46 MODULE_VERSION("v0.1");
linux非实时系统,实时性有待提高,低负荷、毫秒级以上的操作还是比较理想。先来看看这个几乎是个驱动就会涉及到的 异常常见的 结构体 timer_list。
struct timer_list { struct list_head entry; unsigned long expires; void (*function)(unsigned long); unsigned long data; struct tvec_base *base; //系统内有许多timer_list,此指针指向它们共同的管理者 #ifdef CONFIG_TIMER_STATS void *start_site; char start_comm[16]; int start_pid; #endif #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif };
来看看这个struct tvec_base。
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
unsigned long next_timer;
/*级联表*/
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
} ____cacheline_aligned;
关键就在于对struct tvec的理解。至于tvec_root,其实和tvec一摸一样。
struct tvec { struct list_head vec[TVN_SIZE]; }; struct tvec_root { struct list_head vec[TVR_SIZE]; };
有了上述的了解,开始看一些关键函数:
cascade(base, &base->tv2, (base->timer_jiffies >> 8) & 63);
static int cascade(struct tvec_base *base, struct tvec *tv, int index)
{
/* cascade all the timers from tv up one level */
struct timer_list *timer, *tmp;
struct list_head tv_list;
list_replace_init(tv->vec + index, &tv_list);
list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
BUG_ON(tbase_get_base(timer->base) != base);
internal_add_timer(base, timer); //-->
}
return index;
}
internal_add_timer()
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer) { unsigned long expires = timer->expires; unsigned long idx = expires - base->timer_jiffies; struct list_head *vec; if (idx < TVR_SIZE) { //idx < 256 int i = expires & TVR_MASK; vec = base->tv1.vec + i; } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { int i = (expires >> TVR_BITS) & TVN_MASK; vec = base->tv2.vec + i; } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) { int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; vec = base->tv3.vec + i; } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) { int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; vec = base->tv4.vec + i; } else if ((signed long) idx < 0) { /* * Can happen if you add a timer with expires == jiffies, * or you set a timer to go off in the past */ vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK); } else { int i; /* If the timeout is larger than 0xffffffff on 64-bit * architectures then we use the maximum timeout: */ if (idx > 0xffffffffUL) { idx = 0xffffffffUL; expires = idx + base->timer_jiffies; } i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; vec = base->tv5.vec + i; } /* * Timers are FIFO: */ list_add_tail(&timer->entry, vec); }
从一堆的if,if else中,基本可以看出个这样的眉目:
内核将不同时长的定时分了类,从第一个if可以看出,8个字节的为一类,也就是256种状况(0~255jiffies)。
第二类多了6位,也就是64种。以此类推。
至于那个idx > 0xffffffffUL,源于内核更多考虑的是32位构架,至于64位能表示的时长,一般人儿在一般情况下,几乎是用不到di。
最后便是,内核搞了512个链表来实现一个定时器(256个短期链表+4组64个的长期链表),占用了4kb的空间,大么?
上个图比较直观:
这便是定时器的底层实现。多个定时器在同一条链表上,需要合理的排序,使查找更具效率。
从cascade可以看出,目前使用时间轮方式(个人更倾向叫hash);
pjlib 中的定时器在内部使用数组的方式实现最小堆也是一种选择。
定时器又如如何计算剩余时间的呢?可以查看udelay的实现:
#define udelay(n) \
(__builtin_constant_p(n) ? \
((n) > (MAX_UDELAY_MS * 1000) ? __bad_udelay() : \
__const_udelay((n) * ((2199023U*HZ)>>11))) : \
__udelay(n))
#endif /* defined(_ARM_DELAY_H) */
__udelay()与平台相关,进入arch/arm
<arch/arm/lib/delay.S>
ENTRY(__udelay)
ldr r2, .LC1
mul r0, r2, r0
ENTRY(__const_udelay) @ 0 <= r0 <= 0x7fffff06
mov r1, #-1
ldr r2, .LC0
ldr r2, [r2] @ max = 0x01ffffff
add r0, r0, r1, lsr #32-14
mov r0, r0, lsr #14 @ max = 0x0001ffff
add r2, r2, r1, lsr #32-10
mov r2, r2, lsr #10 @ max = 0x00007fff
mul r0, r2, r0 @ max = 2^32-1
add r0, r0, r1, lsr #32-6
movs r0, r0, lsr #6
moveq pc, lr
/*
* loops = r0 * HZ * loops_per_jiffy / 1000000
*
* Oh, if only we had a cycle counter...
*/
@ Delay routine
ENTRY(__delay)
subs r0, r0, #1
#if 0
movls pc, lr
subs r0, r0, #1
movls pc, lr
subs r0, r0, #1
movls pc, lr
subs r0, r0, #1
movls pc, lr
subs r0, r0, #1
movls pc, lr
subs r0, r0, #1
movls pc, lr
subs r0, r0, #1
movls pc, lr
subs r0, r0, #1
#endif
bhi __delay
mov pc, lr
ENDPROC(__udelay)
看汇编耗查克拉,以下是等价的c语言形式:
/*
* loops = usecs * HZ * loops_per_jiffy / 1000000
*
* Oh, if only we had a cycle counter...
*/
void __delay(unsigned long loops)
{
asm volatile(
"1: subs %0, %0, #1 \n"
" bhi 1b \n"
: /* No output */
: "r" (loops)
);
}
EXPORT_SYMBOL(__delay);
/*
* 0 <= xloops <= 0x7fffff06
* loops_per_jiffy <= 0x01ffffff (max. 3355 bogomips)
*/
void __const_udelay(unsigned long xloops)
{
unsigned long mask = ULONG_MAX;
unsigned long lpj = loops_per_jiffy;
unsigned long loops;
xloops += mask >> (32 - 14);
xloops >>= 14; /* max = 0x01ffffff */
lpj += mask >> (32 - 10);
lpj >>= 10; /* max = 0x0001ffff */
loops = lpj * xloops; /* max = 0x00007fff */
loops += mask >> (32 - 6);
loops >>= 6; /* max = 2^32-1 */
if (likely(loops))
__delay(loops);
}
EXPORT_SYMBOL(__const_udelay);
/*
* usecs <= 2000
* HZ <= 1000
*/
void __udelay(unsigned long usecs)
{
__const_udelay(usecs * ((2199023*HZ)>>11));
}
最后,2.6内核由于增强了实时性,对于app也引入了POSIX 1003.1b标准的定时器。相应的在app层增加了一些POSIX接口,更好的支持多线程和实时应用程序。
总之,一堆小闹钟挂在一簇簇的条儿上,过一个time irq,调整这些条儿,同时处理些其他的系统操作,比如增加jiffies,软中断处理等。Linux的arm平台时钟精度应用一般限于微秒级,或者增加外部时钟,达到更加精确的目的,不过,目前似乎没什么必要。
End.