练习怎样编写调用内核的时间测量功能为应用程序测量和精确定时。通过该练习我们可以进一步理解 Linux 内核的定时机制及其数据结构以及怎样从用户空间去访问内核空间的时间数据。
从用户空间去获取系统时间数据需要以下基本代码:
#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 后引发定时中断,产生超时信号通知对应进程定时器时间到,然后定时器重新从设置的初始值再次开始递减计数。
三种定时器都使用 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 //定时器当前值
} ;
三种定时器都使用 getitimer()系统调用获取定时器当前值:
#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 //定时器当前值
} ;
首先我们先根据内核的定时机制,来实现一个测试程序运行时间的例子:
程序是监听用户ctrl+c按键,按下后,打印一次程序从开始运行经历了多长时间了,下面是我的代码实现部分:
/**
* Function: 实现从程序开始程序运行的时间
* 用户每按下一次 ctrl+c键,程序就输出一次程序开始之后的
* 运行时间
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
//定时器中断处理的原函数
static void sigreal(void);
//监听SIGINT信号处理函数
static void sigctrl(void);
//记录运行秒数的数值
static long run_sec = 0;
//记录秒数的结构变量
static struct itimerval realt;
int main(){
signal(SIGINT,sigctrl);
signal(SIGALRM,sigreal);
realt.it_interval.tv_sec = 0;
realt.it_interval.tv_usec = 999999;
realt.it_value.tv_sec = 0;
realt.it_value.tv_usec = 999999;
setitimer(ITIMER_REAL,&realt,NULL);
//测试数据
int temp = 0;
while(1){
temp++;
}
return 0;
}
static void sigreal(void)
{
run_sec++;
}
//SIGINT信号处理函数
static void sigctrl(void)
{
//计算的这个过程不是很明白
getitimer(ITIMER_REAL,&realt);
printf("The run time is: %ld s %ld ms\n",run_sec,(999999-realt.it_value.tv_usec)/1000);
}
下面是我的程序的运行截图:
下面我们再来实现一个闹钟的功能,该闹钟有一个特点就是可以精确到微秒级,就是使用我们的定时机制.用户输入经过多少个小时,多少分钟,多少秒,多少毫秒,多少微秒之后提醒该用户.下面是我的程序实现部分:
/**
* Function: 实现一个微秒级的闹钟
* 使用系统的定时器功能可以使闹钟精确到微秒级别
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
//信号SIGALRM的处理函数原型
static void sigreal(void);
//定时器
struct itimerval realt;
//设置程序是否继续运行
static int is_run = 1;
int main()
{
int h,m,s,ms,us;
printf("Please enter the hours:minutes:seconds:ms:us after!\n");
scanf("%d:%d:%d:%d:%d",&h,&m,&s,&ms,&us);
int seconds = h * 3600 + m * 60 + s;
int uSeconds = ms * 1000 + us;
//定时器设置
realt.it_interval.tv_sec = seconds;
realt.it_interval.tv_usec = uSeconds;
realt.it_value.tv_sec = seconds;
realt.it_value.tv_usec = uSeconds;
signal(SIGALRM,sigreal);
setitimer(ITIMER_REAL,&realt,NULL);
while(is_run){
}
printf("Time over!!\n");
return 0;
}
//收到时间到达的信号之后,结束程序的运行
static void sigreal(void)
{
is_run = 0;
}
下面是程序的运行截图:
最后一个例子,我们使用父进程创建了两个子进程,这三个进程分别运算不同级数的非波那且数列,最后打印出每一个进程各自用了多长时间.
下面是代码实现:
/**
* Function : 测试并发进程执行中的各种时间
* 给定3个非波纳且数列数值,可选在36-45之间
* Author : 陈洪波
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
//父进程的3个定时器中断处理函数原型
static void psig_real(void);
static void psig_virtual(void);
static void psig_prof(void);
//子进程1的三个定时中断处理函数原型
static void c1sig_real(void);
static void c1sig_virtual(void);
static void c1sig_prof(void);
//子进程2的3个定时器中断处理函数原型
static void c2sig_real(void);
static void c2sig_virtual(void);
static void c2sig_prof(void);
//非波纳且数列函数原型
long unsigned int fibonnacci(unsigned int n);
//记录3种定时的秒数的变量
static long p_real_secs = 0,c1_real_secs = 0,c2_real_secs = 0;
static long p_virtual_secs = 0,c1_virtual_secs = 0,c2_virtual_secs = 0;
static long p_prof_secs = 0,c1_prof_secs = 0,c2_prof_secs = 0;
//记录3种定时的毫秒秒数的结构变量
static struct itimerval p_realt,c1_realt,c2_realt;
static struct itimerval p_virtt,c1_virtt,c2_virtt;
static struct itimerval p_proft,c1_proft,c2_proft;
int main(int argc,char **argv)
{
long unsigned fib = 0;
pid_t pid1,pid2;
unsigned int fibarg;
int status;
int i;
if(argc < 3){
printf("Usage: testing arg1 arg2 and arg3!\n");
return 1;
}
//父进程设置3中定时处理函数入口
signal(SIGALRM,psig_real);
signal(SIGVTALRM,psig_virtual);
signal(SIGPROF,psig_prof);
//初始化父进程3种时间定时器
//进程实际经过的时间
p_realt.it_interval.tv_sec = 9;
p_realt.it_interval.tv_usec = 999999;
p_realt.it_value.tv_sec = 9;
p_realt.it_value.tv_usec = 999999;
setitimer(ITIMER_REAL,&p_realt,NULL);
//进程经过的虚拟时间,只有相关进程正在执行时该时间才fibonnacci(unsigned int n);会增加
p_virtt.it_interval.tv_sec = 9;
p_virtt.it_interval.tv_usec = 999999;
p_virtt.it_value.tv_sec = 9;
p_virtt.it_value.tv_usec = 999999;
setitimer(ITIMER_VIRTUAL,&p_virtt,NULL);
//反映进程经过的虚拟时间加上内核为相关进程执行工作的时间之和
p_proft.it_interval.tv_sec = 9;
p_proft.it_interval.tv_usec = 999999;
p_proft.it_value.tv_sec = 9;
p_proft.it_value.tv_usec = 999999;
setitimer(ITIMER_PROF,&p_proft,NULL);
pid1 = fork();
if(pid1 == 0){
//子进程1设置3中定时处理入口
signal(SIGALRM,c1sig_real);
signal(SIGVTALRM,c1sig_virtual);
signal(SIGPROF,c1sig_prof);
//子进程的3种时间定时器
c1_realt.it_interval.tv_sec = 9;
c1_realt.it_interval.tv_usec = 999999;
c1_realt.it_value.tv_sec = 9;
c1_realt.it_value.tv_usec = 999999;
setitimer(ITIMER_REAL,&c1_realt,NULL);
c1_virtt.it_interval.tv_sec = 9;
c1_virtt.it_interval.tv_usec = 999999;
c1_virtt.it_value.tv_sec = 9;
c1_virtt.it_value.tv_usec = 999999;
setitimer(ITIMER_VIRTUAL,&c1_virtt,NULL);
c1_proft.it_interval.tv_sec = 9;
c1_proft.it_interval.tv_usec = 999999;
c1_proft.it_value.tv_sec = 9;
c1_proft.it_value.tv_usec = 999999;
setitimer(ITIMER_PROF,&c1_proft,NULL);
//子进程1开始计算fib
fib = fibonnacci(atoi(argv[1]));
//打印子进程1所花费的3种时间值
getitimer(ITIMER_REAL,&c1_realt);
printf("Child1 fib=%ld\n Child1 Real Time=%ld Sec %ld Msec\n",
fib,c1_real_secs + 9 - c1_realt.it_value.tv_sec,
(999999-c1_realt.it_value.tv_usec)/1000);
getitimer(ITIMER_VIRTUAL,&c1_virtt);
printf("Child1 Virtual Time=%ld sec %ld Msec\n",
c1_virtual_secs+9-c1_virtt.it_value.tv_sec,
(999999-c1_virtt.it_value.tv_usec)/1000);
getitimer(ITIMER_PROF,&c1_proft);
printf("Child1 Prof Time=%ld sec %ld Msec\n\n",
c1_prof_secs+9-c1_proft.it_value.tv_sec,
(999999-c1_proft.it_value.tv_usec)/1000);
}else if((pid2=fork()) == 0){
//子进程2设置3中定时处理入口
signal(SIGALRM,c2sig_real);
signal(SIGVTALRM,c2sig_virtual);
signal(SIGPROF,c2sig_prof);
//子进程2的3种时间定时器
c2_realt.it_interval.tv_sec = 9;
c2_realt.it_interval.tv_usec = 999999;
c2_realt.it_value.tv_sec = 9;
c2_realt.it_value.tv_usec = 999999;
setitimer(ITIMER_REAL,&c2_realt,NULL);
c2_virtt.it_interval.tv_sec = 9;
c2_virtt.it_interval.tv_usec = 999999;
c2_virtt.it_value.tv_sec = 9;
c2_virtt.it_value.tv_usec = 999999;
setitimer(ITIMER_VIRTUAL,&c2_virtt,NULL);
c2_proft.it_interval.tv_sec = 9;
c2_proft.it_interval.tv_usec = 999999;
c2_proft.it_value.tv_sec = 9;
c2_proft.it_value.tv_usec = 999999;
setitimer(ITIMER_PROF,&c2_proft,NULL);
//子进程2开始计算fib
fib = fibonnacci(atoi(argv[2]));
//打印子进程2所花费的3种时间值
getitimer(ITIMER_REAL,&c2_realt);
printf("Child2 fib=%ld\n Child2 Real Time=%ld Sec %ld Msec\n",
fib,c2_real_secs + 9 - c2_realt.it_value.tv_sec,
(999999-c2_realt.it_value.tv_usec)/1000);
getitimer(ITIMER_VIRTUAL,&c2_virtt);
printf("Child2 Virtual Time=%ld sec %ld Msec\n",
c2_virtual_secs+9-c2_virtt.it_value.tv_sec,
(999999-c2_virtt.it_value.tv_usec)/1000);
getitimer(ITIMER_PROF,&c2_proft);
printf("Child2 Prof Time=%ld sec %ld Msec\n\n",
c2_prof_secs+9-c2_proft.it_value.tv_sec,
(999999-c2_proft.it_value.tv_usec)/1000);
}else{
//父进程开始计算fib
fib = fibonnacci(atoi(argv[3]));
//打印父进程所花费的3种时间值
getitimer(ITIMER_REAL,&p_realt);
printf("Parent fib=%ld\n Parent Real Time=%ld Sec %ld Msec\n",
fib,p_real_secs + 9 - p_realt.it_value.tv_sec,
(999999-p_realt.it_value.tv_usec)/1000);
getitimer(ITIMER_VIRTUAL,&p_virtt);
printf("Parent Virtual Time=%ld sec %ld Msec\n",
p_virtual_secs+9-p_virtt.it_value.tv_sec,
(999999-p_virtt.it_value.tv_usec)/1000);
getitimer(ITIMER_PROF,&p_proft);
printf("Parent Prof Time=%ld sec %ld Msec\n\n",
p_prof_secs+9-p_proft.it_value.tv_sec,
(999999-p_proft.it_value.tv_usec)/1000);
//等待子进程结束
waitpid(pid1,&status,0);
waitpid(pid2,&status,0);
}
}
//父进程的3个定时中断处理函数
static void psig_real(void)
{
p_real_secs += 10;
}
static void psig_virtual(void)
{
p_virtual_secs += 10;
}
static void psig_prof(void)
{
p_prof_secs += 10;
}
//子进程1的3个中断处理函数
static void c1sig_real(void)
{
c1_real_secs += 10;
}
static void c1sig_virtual(void)
{
c1_virtual_secs += 10;
}
static void c1sig_prof(void)
{
c1_prof_secs += 10;
}
//子进程2的3个中断处理函数
static void c2sig_real(void)
{
c2_real_secs += 10;
}
static void c2sig_virtual(void)
{
c2_virtual_secs += 10;
}
static void c2sig_prof(void)
{
c2_prof_secs += 10;
}
//非波纳且的递归实现
long unsigned int fibonnacci(unsigned int n)
{
if(n==1 || n==2)
return 1;
return fibonnacci(n-1)+fibonnacci(n-2);
}
下面是程序的运行截图: