三、内核时钟(2)

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(&current->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(&current->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 */
};   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值