1 实验目的
本实验是练习怎样编写调用内核的时间测量功能为应用程序测量和精确定时。通过这个实验我们可以进一步理解 Linux
内核的定时机制及其数据结构以及怎样从用户空间去访问内核空间的时间数据。
2 实验内容
A 在用户态编写一个程序,该程序设定一个定时器,在时间到期的时候做出某种可观察的响应(方法不限)。
B 用定时器 ITIMER_REAL
实现gettimeofday
的功能。使其一秒钟产生一个信号,计算已经过的秒数。
C 记录一个进程运行时所占用的real time
,cpu time
, user time
, kernel time
。
3 实验说明
1) 系统时间的获取和内核定时机制
从用户空间去获取系统时间数据需要以下基本代码:
#include <sys/time>
…
struct timeval{
long tv_sec; //从 1970-1-1 12:到现在经过的秒数
long tv_usec;//从从上 1 秒到现在经过的微秒数
} theTime;
…
gettimeofday(&theTime,NULL); //获取系统时间的系统调用
…
每个进程使用的各种定时器需要Linux
的内部定时器。内部定时器可以跟踪记录3
种不同类型的定时机制,它们反映了不同时间的划分。这三种定时机制具有不同的作用和应用,分别是:
ITIMER_REAL
:反映进程经过的实际时间,这种定时器到时后发 SIGALRM
信号。它与struct task_struct
结构中的it_real_value
和 it_real_incr
字段有关。
ITIMER_VIRTUAL
:反映进程经过的虚拟时间,只有相关进程正在执行时该时间才会增加。这种定时器到时后发 SIGVTALRM 信号。与
struct task_struct 结构中的
it_virt_value和
it_virt_incr字段有关
ITIMER_PROF
:反映进程经过的虚拟时间加上内核为相关进程执行工作的时间之和。这种定时 器 到 时 后 发
SIGPROF 信 号 。 与
struct task_struct 结 构 中 的
it_prof_value 和
it_prof_incr `字段有关。
每个定时器需要周期性的设定一个初始时间值,之后递减计数到 0 后引发定时中断,产生超时信号通知对应进程定时器时间到,然后定时器重新从设置的初始值再次开始递减计数。
2) 定时器的使用
定时器使用:定时器的使用只须执行一些初始化工作,设置一个超时时间,指定超时发生后执行的函数,然后激活定时器就可以了。
定时器的使用步骤
初始化,
设置一个超时时间,
指定超时发生时执行的函数,
激活定时器。
3) 使用定时器时的函数
(1) 初始化:三种定时器都使用setitimer()系统调用进行初始化:
#include <sys/time.h>
…
函数setitimer(
int timerType ,//定时器类型
const struct itimerval *value, //定时器初始的和当前的秒数和毫秒数
struct itimerval *oldValue //该参数可不做处理
)
结构体struct itimerval{
struct timeval it_it_interval; //下一次定时初值。为0定时器停止
struct timeval it_value //定时器当前值
} ;
(2) 三种定时器都使用getitimer()系统调用获取定时器当前值:
#include <sys/time.h>
…
函数getitimer(int timerType ,//定时器类型
const struct itimerval *value, //定时器当前的秒数和毫秒数
)
(3) 各类定时器到时的信号处理函数可以使用系统调用函数指定:
sighandler_t signal(int signum, //信号类型
sighandler_t handler //信号处理函数名
);
4 实验程序:
A 在用户态编写一个程序,该程序设定一个定时器,在时间到期的时候做出某种可观察的响应(方法不限)。
设计思路:
设置定时器 ITIMER_REAL
间隔为一秒钟。并为计时到时设定信号处理程序,使其输出当前所记时间。定时3秒,计时结束,给出提示、终止程序。
程序源代码清单:
/****test3.c*****/
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
static void sighandle(int);
static int second = 0;
int main(){
struct itimerval v;
signal(SIGALRM,sighandle);
v.it_interval.tv_sec = 1;
v.it_interval.tv_usec = 0;
v.it_value.tv_sec = 1;
v.it_value.tv_usec = 0;
setitimer(ITIMER_REAL,&v,NULL);
for(;;);
}
static void sighandle(int s)
{
second++;
if(second<=3)
printf("%d\r",second);
else abort();
fflush(stdout);
}
运行结果:
B 用定时器ITIMER_REAL
实现gettimeofday
的功能。使其一秒钟产生一个信号,计算已经过的秒数。
设计思路:
设置定时器ITIMER_REAL
间隔为一秒钟。并为计时到时设定信号处理程序,即 singal(SIGALRM,⋯)
,使其输出当前所记时间。
程序源代码清单:
/****test1.c*****/
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
static void sighandle(int);
static int second = 0;
int main(){
struct itimerval v;
signal(SIGALRM,sighandle);
v.it_interval.tv_sec = 1;
v.it_interval.tv_usec = 0;
v.it_value.tv_sec = 1;
v.it_value.tv_usec = 0;
setitimer(ITIMER_REAL,&v,NULL);
for(;;);
}
static void sighandle(int s)
{second++;
printf("%d\r",second);
fflush(stdout);
}
运行结果:每隔一秒结果+1
C 记录一个进程运行时所占用的real time
, cpu time
, user time
, kernel time
。
设计思路:
任务开始前设置好定时器ITIMER_REAL
,ITIMER_VIRTUAL
,ITIMER_PROF
,即 其相应的信号处理程序。在任务执行过程中内核定时器通过产生等间隔的信号来记录进程所需的各种时间参量,并在任务结束后打印出来。
程序源代码清单 :
/************test2.c**********/
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
static void sighandle(int);
static long realsecond = 0;
static long vtsecond = 0;
static long profsecond = 0;
static struct itimerval realt,virtt,proft;int main(){
struct itimerval v;
int i,j;
long moresec,moremsec,t1,t2;
signal(SIGALRM,sighandle);
signal(SIGVTALRM,sighandle);
signal(SIGPROF,sighandle);
v.it_interval.tv_sec = 10;
v.it_interval.tv_usec = 0;
v.it_value.tv_sec = 10;
v.it_value.tv_usec = 0;
setitimer(ITIMER_REAL,&v,NULL);
setitimer(ITIMER_VIRTUAL,&v,NULL);
setitimer(ITIMER_PROF,&v,NULL);
for(j= 0;j<1000;j++){
for(i= 0;i<500;i++){printf("********\r");fflush(stdout);}
}
getitimer(ITIMER_PROF,&proft);
getitimer(ITIMER_REAL,&realt);
getitimer(ITIMER_VIRTUAL,&virtt);
printf("\n");
moresec = 10 - realt.it_value.tv_sec;
moremsec = (1000000 - realt.it_value.tv_usec)/1000;
printf("realtime = %ld sec, %ld msec\n",realsecond+moresec,moremsec);
moresec = 10 - proft.it_value.tv_sec;
moremsec = (1000000 - proft.it_value.tv_usec)/1000;
printf("cputime = %ld sec, %ld msec\n",profsecond+moresec,moremsec);
moresec = 10 - virtt.it_value.tv_sec;
moremsec = (1000000 - virtt.it_value.tv_usec)/1000;
printf("usertime = %ld sec, %ld msec\n",vtsecond+moresec,moremsec);
t1 = (10 - proft.it_value.tv_sec)*1000 + (1000000 - proft.it_value.tv_usec)/1000 +
profsecond*10000;
t2 = (10 - virtt.it_value.tv_sec)*1000 + (1000000 - virtt.it_value.tv_usec)/1000 +
vtsecond*10000;
moresec = (t1 - t2)/1000;
moremsec = (t1 - t2) % 1000;
printf("kerneltime = %ld sec, %ld msec\n",moresec,moremsec);
fflush(stdout);}
static void sighandle(int s)
{
switch(s){
case SIGALRM:realsecond+=10;break;
case SIGVTALRM:vtsecond+=10;break;
case SIGPROF:profsecond+=10;break;
default :break;
}
}
运行结果: