linux用户态定时器函数,linux 定时器的实现及其应用

本文深入解析Linux 2.6.11版本中的定时器机制,包括真实间隔定时器、虚拟间隔定时器及PROF间隔定时器的工作原理与实现细节。介绍了定时器的数据结构、关键函数及系统调用的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux 定时器的实现及其应用 (2.6.11)

1, 概念

定时器分为三种,分别是:

*真实间隔定时器(ITIMER_REAL)

*虚拟间隔定时器(ITIMER_VIRTUAL)

*PROF间隔定时器(ITIMER_PROF)

*真实间隔定时器(ITIMER_REAL):这种间隔定时器在启动后,不管进程是否运行,每个时钟滴答都将其间隔计数器减1。当减到0值时,内核向进程发送SIGALRM信号。结构类型task_struct中的成员it_real_incr则表示真实间隔定时器的间隔计数器的初始值,而成员 it_real_value则表示真实间隔定时器的间隔计数器的当前值。由于这种间隔定时器本质上与上一节的内核定时器时一样的,因此Linux实际上是通过real_timer这个内嵌在task_struct结构中的内核动态定时器来实现真实间隔定时器ITIMER_REAL的。

*虚拟间隔定时器ITIMER_VIRT:也称为进程的用户态间隔定时器。结构类型task_struct中成员it_virt_incr和 it_virt_value分别表示虚拟间隔定时器的间隔计数器的初始值和当前值,二者均以时钟滴答次数位计数单位。当虚拟间隔定时器启动后,只有当进程在用户态下运行时,一次时钟滴答才能使间隔计数器当前值it_virt_value减1。当减到0值时,内核向进程发送SIGVTALRM信号(虚拟闹钟信号),并将it_virt_value重置为初值it_virt_incr。具体请do_it_virt()函数的实现。

*PROF间隔定时器ITIMER_PROF:进程的task_struct结构中的it_prof_value和it_prof_incr成员分别表示PROF间隔定时器的间隔计数器的当前值和初始值(均以时钟滴答为单位)。当一个进程的PROF间隔定时器启动后,则只要该进程处于运行中,而不管是在用户态或核心态下执行,每个时钟滴答都使间隔计数器it_prof_value值减1。当减到0值时,内核向进程发送SIGPROF信号,并将 it_prof_value重置为初值it_prof_incr。具体请见do_it_prof()函数。

2, 数据结构

struct timeval {

time_t      tv_sec;     /* seconds */

suseconds_t tv_usec;    /* microseconds */

};

struct itimerval {

struct timeval it_interval; /* timer interval */ //间隔计时器的初始值

struct timeval it_value;    /* current value */  //间隔定时器的当前值

};

struct timer_list {

struct list_head entry; //定时器链表

unsigned long expires; //终止时间

spinlock_t lock;

unsigned long magic;

void (*function)(unsigned long);  //定时器终止时执行的函数

unsigned long data;

struct tvec_t_base_s *base; //定时器基础设施

};

//与进程相关的定时器结构

//成员it_real_incr则表示真实间隔定时器的间隔计数器的初始值,

//成员 it_real_value则表示真实间隔定时器的间隔计数器的当前值。

struct task_struct {

...

unsigned long rt_priority;

unsigned long it_real_value, it_real_incr;

cputime_t it_virt_value, it_virt_incr;

cputime_t it_prof_value, it_prof_incr;

struct timer_list real_timer;

...

}

3, 实现

*初始化

#define INIT_TASK(tsk)  \

...

.real_timer = {                     \

.function   = it_real_fn                \

},

...

}

//该函数设定制定的间隔定时器,并且返回间隔定时器的原来的值。

asmlinkage long sys_setitimer(int which,

struct itimerval __user *value,

struct itimerval __user *ovalue)

{

struct itimerval set_buffer, get_buffer;

int error;

// 若value指针非空,把该值从用户空间复制到内核空间的set_buffer变量中

// 若value指针为空,把变量set_buffer清空

if (value) {

if(copy_from_user(&set_buffer, value, sizeof(set_buffer)))

return -EFAULT;

} else

memset((char *) &set_buffer, 0, sizeof(set_buffer));

//若ovalue指针不为NULL,把内核变量get_buffer的指针值作为第三个参数传入

error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : NULL);

if (error || !ovalue)

return error;

//若get_buffer指针不为NULL,必须把它的值复制到用户空间变量ovalue

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)

{

unsigned long expire;

cputime_t cputime;

int k;

if (ovalue && (k = do_getitimer(which, ovalue)) < 0)

return k;

switch (which) {

case ITIMER_REAL:

//从真实间隔定时器链表中删除原来的间隔定时器

del_timer_sync(&current->real_timer);

//计算定时器终止时间

expire = timeval_to_jiffies(&value->it_value);

//把计算出来的终止时间赋给it_real_value,表示当前值

current->it_real_value = expire;

current->it_real_incr =

timeval_to_jiffies(&value->it_interval);

//value->it_value为0,只是删除老定时器,更新目前进程相关的结构的值

if (!expire)

break;

if (expire > (unsigned long) LONG_MAX)

expire = LONG_MAX;

current->real_timer.expires = jiffies + expire; //更新终止时间

add_timer(&current->real_timer); //把该定时器加入到定时器动态链表中

break;

...

}

//获取定时器剩余的描述

int do_getitimer(int which, struct itimerval *value)

{

register unsigned long val;

switch (which) {

case ITIMER_REAL:

val = 0;

/*

* FIXME! This needs to be atomic, in case the kernel timer happens!

*/

if (timer_pending(&current->real_timer)) { //检测定时器是否已经启动

val = current->real_timer.expires - jiffies;  //计算定时器当前剩余时间

/* look out for negative/zero itimer.. */

if ((long) val <= 0)  //不满1,补足1

val = 1;

}

jiffies_to_timeval(val, &value->it_value); //转换,并保存计算出来的时间值

jiffies_to_timeval(current->it_real_incr, &value->it_interval); //直接保存初始值

break;

...

default:

return(-EINVAL);

}

return 0;

}

void it_real_fn(unsigned long __data)

{

struct task_struct * p = (struct task_struct *) __data; //获得进程描述符指针

unsigned long interval;

send_group_sig_info(SIGALRM, SEND_SIG_PRIV, p);  //发送SIGALRM信号

interval = p->it_real_incr;

if (interval) { //若it_real_incr不为0,重启定时器,把定时器再次加入到定时器链表中

if (interval > (unsigned long) LONG_MAX)

interval = LONG_MAX;

//重启后的终止时间设定为现在的值+interval的值

p->real_timer.expires = jiffies + interval;

add_timer(&p->real_timer); //把定时器再次加入到动态链表

}

}

// alarm系统调用的实现

asmlinkage unsigned long sys_alarm(unsigned int seconds)

{

struct itimerval it_new, it_old;

unsigned int oldalarm;

//alarm不会重复启动定时器,这必须把it_interval设置成0

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;

//添加ITIMER_REAL定时器,并获取老定时器的值

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 */

//不足1秒补足1秒

if ((!oldalarm && it_old.it_value.tv_usec) || it_old.it_value.tv_usec >= 500000)

oldalarm++;

return oldalarm;

}

可以看出若alarm的参数为0,系统将删除老的定时器,并把现在的定时器的值设定为0,而不会发送信号。

也就是说alarm(0),就是取消以前设定的老的定时器。

3.1 说明

我这里分析的事2.6.11版本的,在后来的版本定时器的实现发生了很大的变化,

在高精定时器的实现中,使用了红黑树的数据结构来组织定时器结构,使得查询和添加删除操作的效率

得到了很大的提高。

4, 应用

settimer和alarm共享一个定时器,所以这两个系统调用不能同时使用。

void handle_timer(int sig)

{

if (SIGALRM == sig)

fprintf(stderr, "get SIGALARM SIG\n");

}

int

main(void)

{

char str[1024];

int readn;

struct sigaction sa;

//这里要注意不能使用signal来注册信号,

//原因见Linux信号的实现机制分析。

sa.sa_flags = SA_SIGINFO;

sigemptyset(&sa.sa_mask);

sa.sa_handler = handle_timer;

sigaction(SIGALRM, &sa, NULL);

for ( ; ; ) {

memset(str, 0, sizeof(str));

fprintf(stderr, "please input int number:\n");

alarm(3);

readn = read(STDIN_FILENO, str, sizeof(str)-1);

if (-1 == readn)

fprintf(stderr, "error =[%s]\n", strerror(errno));

fprintf(stderr, "you input: %s\n", str);

alarm(0);

}

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值