linux定时器.docx

上传人:b****2 文档编号:2200133 上传时间:2022-10-27 格式:DOCX 页数:12 大小:125.77KB
下载 相关 举报
linux定时器.docx_第1页
第1页 / 共12页
linux定时器.docx_第2页
第2页 / 共12页
linux定时器.docx_第3页
第3页 / 共12页
linux定时器.docx_第4页
第4页 / 共12页
linux定时器.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

linux定时器.docx

《linux定时器.docx》由会员分享,可在线阅读,更多相关《linux定时器.docx(12页珍藏版)》请在冰豆网上搜索。

linux定时器.docx

linux定时器

目录

一、设计题目与要求1

二、实验思想1

2.1Linux内核对定时器的描述1

2.2Linux内核定时器3

2.3Linux信号signal处理机制6

三、内核定时器机制的实现6

3.1动态定时器机制的初始化6

3.2将定时器插入到链表中7

四、数据结构与模块说明9

4.1定时器使用9

4.2程序流程图10

4.3源程序10

4.4运行结果与运行情况11

五、实验心得13

六、参考文献14

Linux定时器实验

一、设计题目与要求

1.1设计题目:

Linux定时器

1.2设计要求:

通过研究Linux内核的时间函数,学习内核源代码;然后应用这些知识并且使用“信号”机制建立一种定时器实验,每隔多长时间打印出当前系统的时间,并且计算出每次调度的时间。

二、实验思想

2.1Linux内核对定时器的描述

Linux在include/linux/timer.h头文件中定义了数据结构timer_list来描述一个内核定时器:

structtimer_list{

structlist_headlist;

unsignedlongexpires;

unsignedlongdata;

void(*function)(unsignedlong);

};

各数据成员的含义如下:

(1)双向链表元素list:

用来将多个定时器连接成一条双向循环队列。

(2)expires:

指定定时器到期的时间,这个时间被表示成自系统启动以来的时钟滴答计数(也即时钟节拍数)。

当一个定时器的expires值小于或等于jiffies变量时,我们就说这个定时器已经超时或到期了。

在初始化一个定时器后,通常把它的expires域设置成当前expires变量的当前值加上某个时间间隔值(以时钟滴答次数计)。

(3)函数指针function:

指向一个可执行函数。

当定时器到期时,内核就执行function所指定的函数。

而data域则被内核用作function函数的调用参数。

内核函数init_timer()用来初始化一个定时器。

实际上,这个初始化函数仅仅将结构中的list成员初始化为空。

如下所示(include/linux/timer.h):

staticinlinevoidinit_timer(structtimer_list*timer)

{

timer->list.next=timer->list.prev=NULL;

}

由于定时器通常被连接在一个双向循环队列中等待执行(此时我们说定时器处于pending状态)。

因此函数time_pending()就可以用list成员是否为空来判断一个定时器是否处于pending状态。

如下所示(include/linux/timer.h):

staticinlineinttimer_pending(conststructtimer_list*timer)

{returntimer->list.next!

=NULL;}

时间比较操作在定时器应用中经常需要比较两个时间值,以确定timer是否超时,所以Linux内核在timer.h头文件中定义了4个时间关系比较操作宏。

这里我们说时刻a在时刻b之后,就意味着时间值a≥b。

Linux强烈推荐用户使用它所定义的下列4个时间比较操作宏(include/linux/timer.h):

#definetime_after(a,b)((long)(b)-(long)(a)<0)

#definetime_before(a,b)time_after(b,a)

#definetime_after_eq(a,b)((long)(a)-(long)(b)>=0)

#definetime_before_eq(a,b)time_after_eq(b,a)

2.2Linux内核定时器

定时器是管理内核时间的基础,用来计算流逝的时间,它以某种频率(节拍率)自行触发时钟中断,当时钟中断发生时,内核就通过一种特殊中断处理程序对其进行处理。

但是原来的实现只能是time_tmytime形式的,经过简单的localtime(mytime)和ctime(&mytime)处理.精度是不够的,为了返回高精度的时间,这里使用了gettimeofday函数。

这个syscall用来供用户获取timeval格式的当前时间信息(精确度为微秒级),以及系统的当前时区信息(timezone)。

结构类型timeval的指针参数tv指向接受时间信息的用户空间缓冲区,参数tz是一个timezone结构类型的指针,指向接收时区信息的用户空间缓冲区。

这两个参数均为输出参数,返回值0表示成功,返回负值表示出错。

函数sys_gettimeofday()的源码如下(kernel/time.c):

asmlinkagelongsys_gettimeofday(structtimeval*tv,structtimezone*tz)

{

if(tv){

structtimevalktv;

do_gettimeofday(&ktv);

if(copy_to_user(tv,&ktv,sizeof(ktv)))

return-EFAULT;

}

if(tz){

if(copy_to_user(tz,&sys_tz,sizeof(sys_tz)))

return-EFAULT;

}

return0;

}

显然,函数的实现主要分成两个大的方面:

(1)如果tv指针有效,则说明用户要以timeval格式来检索系统当前时间。

为此,先调用do_gettimeofday()函数来检索系统当前时间并保存到局部变量ktv中。

然后再调用copy_to_user()宏将保存在内核空间中的当前时间信息拷贝到由参数指针tv所指向的用户空间缓冲区中。

(2)如果tz指针有效,则说明用户要检索当前时区信息,因此调用copy_to_user()宏将全局变量sys_tz中的时区信息拷贝到参数指针tz所指向的用户空间缓冲区中。

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

函数do_gettimeofday()的源码如下(arch/i386/kernel/time.c):

voiddo_gettimeofday(structtimeval*tv)

{

unsignedlongflags;

unsignedlongusec,sec;

read_lock_irqsave(&xtime_lock,flags);

usec=do_gettimeoffset();

{

unsignedlonglost=jiffies-wall_jiffies;

if(lost)

usec+=lost*(1000000/HZ);

}

sec=xtime.tv_sec;

usec+=xtime.tv_usec;

read_unlock_irqrestore(&xtime_lock,flags);

while(usec>=1000000){

usec-=1000000;

sec++;

}

tv->tv_sec=sec;

tv->tv_usec=usec;

}

该函数的完成实际的当前时间检索工作。

由于gettimeofday()系统调用要求时间精度要达到微秒级,因此do_gettimeofday()函数不能简单地返回xtime中的值即可,而必须精确地确定自从时钟驱动的BottomHalf上一次更新xtime的那个时刻到do_gettimeofday()函数的当前执行时刻之间的具体时间间隔长度,以便精确地修正xtime的值。

假定被do_gettimeofday()用来修正xtime的时间间隔为fixed_usec,而从wall_jiffies到jiffies之间的时间间隔是lost_usec,而从jiffies到do_gettimeofday()函数的执行时刻的时间间隔是offset_usec。

则下列三个等式成立:

fixed_usec=(lost_usec+offset_usec)lost_usec=(jiffies-wall_jiffies)*TICK_SIZE=(jiffies-wall_jiffies)*(1000000/HZ)

由于全局变量last_tsc_low表示上一次时钟中断服务函数timer_interrupt()执行时刻的CPUTSC寄存器的值,因此我们可以用X86CPU的TSC寄存器来计算offset_usec的值。

也即:

offset_usec=delay_at_last_interrupt+(current_tsc_low-last_tsc_low)*fast_gettimeoffset_quotient

其中,delay_at_last_interrupt是从上一次发生时钟中断到timer_interrupt()服务函数真正执行时刻之间的时间延迟间隔。

每一次timer_interrupt()被执行时都会计算这一间隔,并利用TSC的当前值更新last_tsc_low变量。

假定current_tsc_low是do_gettimeofday()函数执行时刻TSC的当前值,全局变量fast_gettimeoffset_quotient则表示TSC寄存器每增加1所代表的时间间隔值,它是由time_init()函数所计算的。

2.3Linux信号signal处理机制

信号signal机制是进程之间相互传递消息的一种方法,全称为软中断信号。

系统调用signal用来设定某个信号的处理方法,其调用声明的格式如下:

void(*signal(intsignum,void(*handler)(int)))(int);成功则返回该信号以前的处理配置,出错则返回SIG_ERR。

在使用该调用的进程中加入以下头文件:

几个常见信号:

SIGINT:

当用户按某些终端键时,引发终端产生的信号.如Ctrl+C键,这将产生中断信号(SIGINT),它将停止一个已失去控制的程序。

SIGSEGV:

由硬件异常(除数为0,无效的内存引用等等)产生的信号。

这些条件通常由硬件检测到,并将其通知内核,然后内核为该条件发生时正在运行的进程产生该信号。

SIGALRM:

进程所设置的闹钟时钟超时的时候产生。

SIGVTALRM:

虚拟时钟超时时产生该信号。

类似于SIGALRM,但是它只计算该进程占用的CPU时间。

SIGPROF:

类似于SIGVTALRM,它不仅包括该进程占用的CPU时间还包括执行系统调用的时间。

三、内核定时器机制的实现

3.1动态定时器机制的初始化

函数init_timervecs()实现对动态定时器机制的初始化。

该函数仅被sched_init()初始化例程所调用。

动态定时器机制初始化过程的主要任务就是将tv1、tv2、…、tv5这5个结构变量中的定时器向量指针数组vec[]初始化为NULL。

如下所示(kernel/timer.c):

voidinit_timervecs(void)

{

inti;

for(i=0;i

INIT_LIST_HEAD(tv5.vec+i);

INIT_LIST_HEAD(tv4.vec+i);

INIT_LIST_HEAD(tv3.ve

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1