1、用户层,系统调用sys_setitimer从用户空间得到初始化定时器的值,然后调用do_setitimer设定定时器,并将其挂到定时器链表上。
linux/kernel/itimer
//include/asm/linkage.h里面的定义:
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
//C语言编译则CPP_ASMLINKAGE为空,__attribute__是关键字,是gcc的C语言扩展,regparm(0)表示不从寄存器传递参数
asmlinkage long sys_setitimer(int which, struct itimerval *value,
struct itimerval *ovalue)
{
if (value) {
if(copy_from_user(&set_buffer, value, sizeof(set_buffer)))
return -EFAULT;
} else
memset((char *) &set_buffer, 0, sizeof(set_buffer));
error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : 0);
if (copy_to_user(ovalue, &get_buffer, sizeof(get_buffer)))
return -EFAULT;
return 0;
}
int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
{
i = tvtojiffies(&value->it_interval);
j = tvtojiffies(&value->it_value);
if (ovalue && (k = do_getitimer(which, ovalue)) < 0)
return k;
switch (which) {
case ITIMER_REAL:
del_timer_sync(¤t->real_timer);
current->it_real_value = j;
current->it_real_incr = i;
if (!j)
break;
if (j > (unsigned long) LONG_MAX)
j = LONG_MAX;
i = j + jiffies;
current->real_timer.expires = i;
add_timer(¤t->real_timer);
break;
case ITIMER_VIRTUAL:
if (j)
j++;
current->it_virt_value = j;
current->it_virt_incr = i;
break;
case ITIMER_PROF:
if (j)
j++;
current->it_prof_value = j;
current->it_prof_incr = i;
break;
default:
return -EINVAL;
}
return 0;
}
时钟中断irq0->timer_interrupt,bottom half 处理函数timer_bh()调用run_timer_list(),判断定时器时间到达。run_timer_list()首先判断tv1.index的值,若tv1.index非0,则唤醒tv1.vec[tv1.index]上定时器timer_list,然后递增timer_jiffies和tv1.index。如果tv1.index为0则调用cascade_timers从tves[1],也就是tv2中搬一些分散插入tv1。如果tv2为空,则从tv3搬些到tv1和tv2以此类推。
这里的timer_jiffies是定时器专有的时钟,基本和jiffies一样,但可能由于有别的bottom half再执行的原因,timer_jiffies得不到及时更新。
linux/kernel/timer.c
static inline void run_timer_list(void)
{
spin_lock_irq(&timerlist_lock);
while ((long)(jiffies - timer_jiffies) >= 0) {
struct list_head *head, *curr;
if (!tv1.index) {
int n = 1;
do {
//tv1中定时器都用光,从 tvecs[n]搬一组分散插入到tvecs[n-1]的各个链表中
cascade_timers(tvecs[n]);
} while (tvecs[n]->index == 1 && ++n < NOOF_TVECS);
}
repeat:
head = tv1.vec + tv1.index;
curr = head->next;
if (curr != head) {
struct timer_list *timer;
void (*fn)(unsigned long);
unsigned long data;
timer = list_entry(curr, struct timer_list, list);
fn = timer->function;
data= timer->data;
detach_timer(timer);
timer->list.next = timer->list.prev = NULL;
timer_enter(timer);
spin_unlock_irq(&timerlist_lock);
fn(data);
spin_lock_irq(&timerlist_lock);
timer_exit();
goto repeat;
}
++timer_jiffies;
tv1.index = (tv1.index + 1) & TVR_MASK;
}
spin_unlock_irq(&timerlist_lock);
}
2、定时器的系统调用
sys_开头的函数为系统调用,如系统调用setitimer发生,产生软中断进入内核,中断处理函数system_call()函数检查系统调用号,到系统调用表sys_call_table中找到该系统调用(号)对应的内核函数入口,接着调用这个内核函数,然后返回。
sys_call_table位于linux\arch\alpha\kernel\entry.S
sys_call_table:
.......
.quad sys_getrlimit
.quad sys_setrlimit /* 145 */
//which选择时钟类型,value为修改后的定时器时间,ovalue为修改前的定时器时间
asmlinkage long sys_setitimer(int which, struct itimerval *value,
struct itimerval *ovalue)
asmlinkage long sys_getitimer(int which, struct itimerval *value)
//定时器初始值和当前值
struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};