|
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中,作为查询结果返回。
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秒)。 Linux联盟收集整理
 |
频道声明:本频道的文章除部分特别声明禁止转载的专稿外,可以自由转载.但请务必注明出出处和原始作者 文章版权归本频道与文章作者所有.对于被频道转载文章的个人和网站,我们表示深深的谢意。
| 原始作者:佚名 |
录入时间:2007-1-2 4:07:48 |
| 信息来源:不详 |
投稿信箱:itqoo@126.com |
|
|
 |
|