首 页文章中心

Linux学习网

您的位置Linux学习网 > Linux综合 > 文章内容

Linux系统进程间隔定时器Itimer(下)

作者:佚名  来源:不详  发布时间:2007-12-21 16:42:00

7.7.3.1 getitimer()系统调用的实现

函数sys_getitimer()有两个参数:(1)which,指定查询调用进程的哪一个间隔定时器,其取值可以是ITIMER_REAL、ITIMER_VIRT和ITIMER_PROF三者之一。(2)value指针,指向用户空间中的一个itimerval结构,用于接收查询结果。该函数的源码如下:

/* SMP: Only we modify our itimer values. */ asmlinkage long sys_getitimer(int which, struct itimerval *value) { int error = -EFAULT; struct itimerval get_buffer; if (value) { error = do_getitimer(which, &get_buffer); if (!error && copy_to_user(value, &get_buffer, sizeof(get_buffer))) error = -EFAULT; } return error; }

显然,sys_getitimer()函数主要通过do_getitimer()函数来查询当前进程的间隔定时器信息,并将查询结果保存在内核空间的结构变量get_buffer中。然后,调用copy_to_usr()宏将 get_buffer中结果拷贝到用户空间缓冲区中。

函数do_getitimer()的源码如下(kernel/itimer.c):

int do_getitimer(int which, struct itimerval *value) { register unsigned long val, interval; switch (which) { case ITIMER_REAL: interval = current->it_real_incr; val = 0; /* * FIXME! This needs to be atomic, in case the kernel timer happens! */ if (timer_pending(¤t->real_timer)) { val = current->real_timer.expires - jiffies; /* look out for negative/zero itimer.. */ if ((long) val <= 0) val = 1; } break; case ITIMER_VIRTUAL: val = current->it_virt_value; interval = current->it_virt_incr; break; case ITIMER_PROF: val = current->it_prof_value; interval = current->it_prof_incr; break; default: return(-EINVAL); } jiffiestotv(val, &value->it_value); jiffiestotv(interval, &value->it_interval); return 0; }

查询的过程如下:

(1)首先,用局部变量val和interval分别表示待查询间隔定时器的间隔计数器的当前值和初始值。

(2)如果which=ITIMER_REAL,则查询当前进程的 ITIMER_REAL间隔定时器。于是从current->it_real_incr中得到ITIMER_REAL间隔定时器的间隔计数器的初始值,并将其保存到interval局部变量中。而对于间隔计数器的当前值,由于ITITMER_REAL间隔定时器是通过real_timer这个内核动态定时器来实现的,因此不能通过current->it_real_value来获得ITIMER_REAL间隔定时器的间隔计数器的当前值,而必须通过real_timer来得到这个值。为此先用timer_pending()函数来判断current->real_timer是否已被起动。如果未启动,则说明ITIMER_REAL间隔定时器也未启动,因此其间隔计数器的当前值肯定是0。因此将val变量简单地置0就可以了。如果已经启动,则间隔计数器的当前值应该等于(timer_real.expires-jiffies)。

(3)如果which=ITIMER_VIRT,则查询当前进程的ITIMER_VIRT间隔定时器。于是简单地将计数器初值it_virt_incr和当前值it_virt_value分别保存到局部变量interval和val中。

(4)如果which=ITIMER_PROF,则查询当前进程的ITIMER_PROF间隔定时器。于是简单地将计数器初值it_prof_incr和当前值it_prof_value分别保存到局部变量interval和val中。

(5)最后,通过转换函数jiffiestotv()将val和interval转换成timeval格式的时间值,并保存到value->it_value和value->it_interval中,作为查询结果返回。 vxbLinux联盟

7.7.3.2 setitimer()系统调用的实现

函数sys_setitimer()不仅设置调用进程的指定间隔定时器,而且还返回该间隔定时器的原有信息。它有三个参数:(1)which,含义与sys_getitimer()中的参数相同。(2)输入参数value,指向用户空间中的一个itimerval结构,含有待设置的新值。(3)输出参数ovalue,指向用户空间中的一个itimerval结构,用于接收间隔定时器的原有信息。

该函数的源码如下(kernel/itimer.c):

/* SMP: Again, only we play with our itimers, and signals are SMP safe * now so that is not an issue at all anymore. */ asmlinkage long sys_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) { struct itimerval set_buffer, get_buffer; int error; 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 (error || !ovalue) return error; if (copy_to_user(ovalue, &get_buffer, sizeof(get_buffer))) return -EFAULT; return 0; }

对该函数的注释如下:

(1)在输入参数指针value非空的情况下,调用copy_from_user()宏将用户空间中的待设置信息拷贝到内核空间中的set_buffer结构变量中。如果value指针为空,则简单地将set_buffer结构变量全部置0。

(2)调用do_setitimer()函数完成实际的设置操作。如果输出参数 ovalue指针有效,则以内核变量get_buffer的地址作为do_setitimer()函数的第三那个调用参数,这样当 do_setitimer()函数返回时,get_buffer结构变量中就将含有当前进程的指定间隔定时器的原来信息。Do_setitimer()函数返回0值表示成功,非0值表示失败。

(3)在do_setitimer()函数返回非0值的情况下,或者ovalue指针为空的情况下(不需要输出间隔定时器的原有信息),函数就可以直接返回了。

(4)如果ovalue指针非空,调用copy_to_user()宏将get_buffer()结构变量中值拷贝到ovalue所指向的用户空间中去,以便让用户得到指定间隔定时器的原有信息值。

函数do_setitimer()的源码如下(kernel/itimer.c):

int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) { register unsigned long i, j; int k; 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; }

对该函数的注释如下:

(1)首先调用tvtojiffies()函数将timeval格式的初始值和当前值转换成以时钟滴答为单位的时间值。并分别保存在局部变量i和j中。

(2)如果ovalue指针非空,则调用do_getitimer()函数查询指定间隔定时器的原来信息。如果do_getitimer()函数返回负值,说明出错。因此就要直接返回错误值。否则继续向下执行开始真正地设置指定的间隔定时器。

(3)如果which=ITITMER_REAL,表示设置ITIMER_REAL 间隔定时器。(a)调用del_timer_sync()函数(该函数在单CPU系统中就是del_timer()函数)将当前进程的 real_timer定时器从内核动态定时器链表中删除。(b)将it_real_incr和it_real_value分别设置为局部变量i和j。(c)如果j=0,说明不必启动real_timer定时器,因此执行break语句退出switch…case控制结构,而直接返回。(d)将 real_timer的expires成员设置成(jiffies+当前值j),然后调用add_timer()函数将当前进程的real_timer定时器加入到内核动态定时器链表中,从而启动该定时器。

(4)如果which=ITIMER_VIRT,则简单地用局部变量i和j的值分别更新it_virt_incr和it_virt_value就可以了。

(5)如果which=ITIMER_PROF,则简单地用局部变量i和j的值分别更新it_prof_incr和it_prof_value就可以了。

(6)最后,返回0值表示成功。

7.7.3.3 alarm系统调用

系统调用alarm可以让调用进程在指定的秒数间隔后收到一个SIGALRM信号。它只有一个参数seconds,指定以秒数计的定时间隔。函数sys_alarm()的源码如下(kernel/timer.c):

/* * For backwards compatibility? This can be done in libc so Alpha * and all newer ports shouldn't need it. */ asmlinkage unsigned long sys_alarm(unsigned int seconds) { struct itimerval it_new, it_old; unsigned int oldalarm; it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; it_new.it_value.tv_sec = seconds; it_new.it_value.tv_usec = 0; do_setitimer(ITIMER_REAL, &it_new, &it_old); oldalarm = it_old.it_value.tv_sec; /* ehhh.. We can't return 0 if we have an alarm pending.. */ /* And we'd better return too much than too little anyway */ if (it_old.it_value.tv_usec) oldalarm++; return oldalarm; }

这个系统调用实际上就是启动进程的ITIMER_REAL间隔定时器。因此它完全可放到用户空间的C函数库(比如libc和glibc)中来实现。但是为了保此内核的向后兼容性,2.4.0版的内核仍然将这个syscall放在内核空间中来实现。函数sys_alarm()的实现过程如下:

(1)根据参数seconds的值构造一个itimerval结构变量it_new。注意!由于alarm启动的ITIMER_REAL间隔定时器是一次性而不是循环重复的,因此it_new变量中的it_interval成员一定要设置为0。

(2)调用函数do_setitimer()函数以新构造的定时器it_new来启动当前进程的ITIMER_REAL定时器,同时将该间隔定时器的原定时间隔保存到局部变量it_old中。

(3)返回值oldalarm表示以秒数计的ITIMER_REAL间隔定时器的原定时间隔值。因此先把it_old.it_value.tv_sec赋给oldalarm,并且在it_old.it_value.tv_usec非0的情况下,将oldalarm的值加1(也即不足1秒补足1秒)。

收藏本页到: 365Key | del.icio.us | | 添加到雅虎收藏+
  • 网站帮助 - 广告合作 - 网站地图