Linux下的时间概念
时间:2010-04-28 来源:Liuqz2009
这一章我们学习Linux的时间表示和计算函数
时间的表示
时间的测量
计时器的使用
1.时间表示 在程序当中,我们经常要输出系统当前的时间,比如我们使用date命令的输出结果。这个时候我们可以使用下面两个函数
#include
time_t time(time_t *tloc);
char *ctime(const time_t *clock);
time 函数返回从1970年1月1日0点以来的秒数。存储在time_t结构之中。不过这个函数的返回值对于我们来说没有什么实际意义。这个时候我们使用第二个 函数将秒数转化为字符串。 这个函数的返回类型是固定的:一个可能值为。 Thu Dec 7 14:58:59 2000 这个字符串的长度是固定的为26
2.时间的测量 有时候我们要计算程序执行的时间。比如我们要对算法进行时间分析。这个时候可以使用下面这个函数。
#include
int gettimeofday(struct timeval *tv,struct timezone *tz);
strut timeval {
long tv_sec; /* 秒数 */
long tv_usec; /* 微秒数 */
};
gettimeofday将时间保存在结构tv之中。tz一般我们使用NULL来代替。
#include
#include
#include
void function()
{
unsigned int i,j;
double y;
for(i=0;i<1000;i++)
for(j=0;j<1000;j++)
y=sin((double)i);
}
main()
{
struct timeval tpstart,tpend;
float timeuse;
gettimeofday(&tpstart,NULL);
function();
gettimeofday(&tpend,NULL);
timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+
tpend.tv_usec-tpstart.tv_usec;
timeuse/=1000000;
printf("Used Time:%fn",timeuse);
exit(0);
}
这个程序输出函数的执行时间,我们可以使用这个来进行系统性能的测试,或者是函数算法的效率分析。在我机器上的一个输出结果是: Used Time:0.556070
3.计时器的使用 Linux操作系统为每一个进程提供了3个内部间隔计时器。
ITIMER_REAL:减少实际时间。到时的时候发出SIGALRM信号。
ITIMER_VIRTUAL:减少有效时间(进程执行的时间)。产生SIGVTALRM信号。
ITIMER_PROF:减少进程的有效时间和系统时间(为进程调度用的时间)。这个经常和上面一个使用用来计算系统内核时间和用户时间。产生 SIGPROF信号。
具体的操作函数是:
#include
int getitimer(int which,struct itimerval *value);
int setitimer(int which,struct itimerval *newval,
struct itimerval *oldval);
struct itimerval {
struct timeval it_interval;
struct timeval it_value;
}
getitimer 函数得到间隔计时器的时间值。保存在value中 setitimer函数设置间隔计时器的时间值为newval。并将旧值保存在oldval中。 which表示使用三个计时器中的哪一个。 itimerval结构中的it_value是减少的时间,当这个值为0的时候就发出相应的信号了。 然后设置为it_interval值。
#include
#include
#include
#include
#include
#define PROMPT "时间已经过去了两秒钟na"
char *prompt=PROMPT;
unsigned int len;
void prompt_info(int signo)
{
write(STDERR_FILENO,prompt,len);
}
void init_sigaction(void)
{
struct sigaction act;
act.sa_handler=prompt_info;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGPROF,&act,NULL);
}
void init_time()
{
struct itimerval value;
value.it_value.tv_sec=2;
value.it_value.tv_usec=0;
value.it_interval=value.it_value;
setitimer(ITIMER_PROF,&value,NULL);
}
int main()
{
len=strlen(prompt);
init_sigaction();
init_time();
while(1);
exit(0);
}
这个程序每执行两秒中之后会输出一个提示。
![]() |
高精度计时器、时间间隔计时器
Dinakar Guniguntala (dgunigun-at-in.ibm.com), 资深软件工程师, IBM 2004 年 9 月 24 日 Linux 是新千年里最杰出的操作系统,而传统的操作系统,如 OS/2 ®,现在正在逐渐淘汰出局。本系列文章是为那些正经受迁移/移植痛苦的开发人员撰写的,可以帮助他们将 OS/2系统驱动和应用程序移植到 Linux 上。本系列文章共分为 3 期,这是是最后一期,重点介绍了如何在 OS/2 和 Linux 中进行计时器调用和 DLL 调用,着眼于两系统之间的映射。本系列文章希望能够成为在 OS/2 和 Linux 之间进行迁移的一个参考。在迁移过程中任何时候您都可 以来参考它,不过最好是在计划阶段,因为它提供了您在设计迁移方案和策略时应该了解的技巧与警告。 这一次您将研究 OS/2 和 Linux 中的计时器调用和 DLL 调用,还有两个系统之间的映射。 在本系列文章的第 1 部分和 第 2 部分,您已经了解了线程、 semaphores 和 mutexes 等同步机制、内存管理、各种 IPC 机制以及文件管理。 首先,我们来看几种类型的计时器:
在 OS/2 中, DosStartTimer 会启动一个重复性的时间间隔计时器,它在每 一个计时器间隔中都会发出一个事件信号量。Linux 没有可以直接与之对应的重复性地发出信号量的调用, 但是您可以组合使用 setitimer 系统调用和 Linux 信号处理机制来实现 这一功能。 在 OS/2 中,计时器值参数的单位是毫秒。在 Linux 中,这一信息由一个 struct itimerval 类型的对象来指定,它的单位既可以是秒也 可以是毫秒。
在 OS/2 中, DosStartTimer() 系统调用会启动一个异步的、 重复性的时间间隔计时器: APIRETDosStartTimer(ULONG msec, HSEM hsem, PHTIMER phtimer);
Linux 没有直接与之对应的每隔一定时间重复发出信号量的 API 调用。使用时间间隔计时器来实现此 功能。时间间隔计时器向进程发送一个 SIGALRM 信号。事件信号量被发送到 信号处事器中,或者等待被发送的信号量的代码位于事件处理器例程之中:
int setitimer (int mode, const struct itimerval *newvalue, struct itimerval
如果成功,则 setitimer 返回 0(-1 表示错误),并将 errno 设置为适当的值。计时器值由第一个参数 newvalue 指定,其类型为 struct itimerval 。当 newvalue->it_value 非零时, 它会指明下一次计时器过期的时间。当 newvalue ->it_interval 非零时,它指定当计时器过期时重新加载 newvalue ->it_value 所用的值。 在信号处理器中只使用同步安全的(async-safe)函数。例如,在信号处理器中不要使用 pthread 条件变量。 在 OS/2 中, DosStopTimer() 会终止一个异步计时器: APIRETDosStopTimer(HTIMER htimer);
在 Linux 中,要终止一个时间间隔计算器,首先调用宏 timerclear 来清空结构体 itimerval 的 it_value 域。 然后使用一个清空过的 itimerval 来调用 setitimer 。 将 it_value 设置为 0 可以禁用计时器,不必理会 it_interval 的值。将 it_interval 设置为 0 可以在下一次过期后禁用计时器(假定 it_value 为非零)。 timerclear 是一个宏定义(在 sys/time.h 中): #define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) 清单 1 中的代码启动一个在每个计时器间隔重复发出一个事件信号量的异步计时器。 清单 1. OS/2 时间间隔计时器示例
清单 2 中的代码启动一个在每个计时器间隔重复发出一个事件信号量的异步计时器。 清单 2. Linux 时间间隔计时器示例
接下来,我们将向您介绍如何获得高精度计时器的当前计时器计数和频率。在 OS/2 中, DosTmrQueryTime() 会返回 Intel 8254 计时器(可编程中断计时器)的时间戳 计数器。Linux 没有可用的 API 可以获得这一信息。您可以使用在 <asm/timex.h> 内核头文件中定义的 get_cycles() 内联函数。 联合时钟的频率使用时间戳计数器,因为计数器与频率的比值在不同的时钟上是相同的。
在 OS2 中, DosTmrQueryTime() 系统调用从 IRQ0 高精度计时器(Intel 8254)获得 高精度计时器计数的一个快照: APIRET DosTmrQueryTime(PQWORD pqwTmrTime);
在 Linux 中, get_cycles() 内联函数返回系统时钟的时间戳计数器, 它是一个 64 位值。这个函数总是成功的(本质上讲,它执行了一个机器指令): static inline cycles_t get_cycles (void); 在 OS2 中, DosTmrQueryFreq() 获得 IRQ0 高精度计时器(Intel 8254) 的频率: APIRET DosTmrQueryFreq (PULONG pulTmrFreq)
Linux 没有与 DosTmrQueryFreq 相当的调用,但是您可以通过 /proc/cpuinfo 获得 时钟频率。 清单 3 展示了一个用来获取 CPU 频率的用户自定义函数。 它打开 /proc/cpuinfo 文件,搜索“cpu MHz”字符串,然后读取 CPU 频率。这个函数将读取的值的单位转换 为 Hz,然后返回转换后的值。 清单 3. 获取时钟频率的 Linux 实现
清单 4 展示了 DosTmrQueryTime 和 DosTmrQueryFreq 调用的使用。 清单 4. OS/2 计时器查询示例
清单 5 展示了如何通过映射 OS/2 DosTmrQueryTime 和 DosTmrQueryFreq 调用来测量两个操作之间的时间间隔。 清单 5. Linux 计时器查询示例
|