本节介绍可用于 SunOS 中的实时应用程序的计时功能。使用这些机制的实时应用程序需要本节中所列出例程的手册页中的详细信息。
SunOS 的计时接口分为两种不同类型:时间标记和间隔计时器。时间标记接口提供已用时间的测量结果。另外,通过时间标记接口,应用程序可以测量状态的持续时间或者事件之间的时间。通过间隔计时器,可以在指定时间唤醒应用程序,并可基于所用时间计划活动。
两个接口均可提供时间标记。gettimeofday(3C) 以 timeval 结构提供当前时间,表示自格林威治标准时间 1970 年 1 月 1 日午夜以来的时间(以秒和微秒为单位)。clock_gettime(clockid 为 CLOCK_REALTIME)以 timespec 结构提供当前时间,表示 gettimeofday(3C) 所返回的相同时间间隔(以秒和纳秒为单位)。
SunOS 使用硬件定期计时器。对于某些工作站,硬件定期计时器是计时信息的唯一来源。如果硬件定期计时器是计时信息的唯一来源,则时间标记的精确度将限制为计时器的分辨率。对于其他平台,分辨率为 1 微秒的计时器寄存器表示时间标记精确到 1 微秒。
通常,实时应用程序使用间隔计时器来计划操作。间隔计时器可以分为以下两种类型:一次性类型或定期类型。
一次性计时器是已经过设置的计时器,设置为相对于当前时间或绝对时间的到期时间。计时器会到期一次然后解除设置。数据传输到存储器中或者使操作超时之后,即可使用此类型的计时器来清除缓冲区。
定期计时器设置有初始到期时间(绝对或相对)以及重复间隔。每次间隔计时器到期时,都会按照重复间隔重新装入此计时器。然后,重新设置此计时器。此计时器可帮助进行数据日志记录或伺服控制。调用间隔计时器接口时,会将小于计时器分辨率的时间值向上舍入为下一个硬件计时器间隔的倍数。此间隔通常为 10 ms。
SunOS 具有两组计时器接口。setitimer(2) 和 getitimer(2) 接口运行固定设置的计时器,此类计时器称为 BSD 计时器,使用 timeval 结构指定时间间隔。使用 timer_create(3RT) 创建的 POSIX 计时器用于运行 POSIX 时钟 CLOCK_REALTIME。POSIX 计时器操作以 timespec 结构表示。
getitimer(2) 和 setitimer(2) 函数分别用于检索和确定指定的 BSD 间隔计时器的值。可用于进程的三个 BSD 间隔计时器包括指定了 ITIMER_REAL 的实时计时器。如果 BSD 计时器已进行设置并允许到期,则系统会向设置此计时器的进程发送适当的信号。
timer_create(3RT) 例程最多可以创建 TIMER_MAX 个 POSIX 计时器。调用方可以指定计时器到期时应向进程发送的信号和关联值。timer_settime(3RT) 和 timer_gettime(3RT) 例程分别用于检索和确定指定的 POSIX 间隔计时器的值。所需的信号暂挂传送时,POSIX 计时器便会到期。此时会对计时器到期的次数进行计数,并且 timer_getoverrun(3RT) 将检索此计数。timer_delete(3RT) 用于解除分配 POSIX 计时器。
以下示例说明了如何使用 setitimer(2) 生成定期中断,以及如何控制计时器中断的到达。
#include <unistd.h> #include <signal.h> #include <sys/time.h> #define TIMERCNT 8 void timerhandler(); int timercnt; struct timeval alarmtimes[TIMERCNT]; main() { struct itimerval times; sigset_t sigset; int i, ret; struct sigaction act; siginfo_t si; /* block SIGALRM */ sigemptyset (&sigset); sigaddset (&sigset, SIGALRM); sigprocmask (SIG_BLOCK, &sigset, NULL); /* set up handler for SIGALRM */ act.sa_action = timerhandler; sigemptyset (&act.sa_mask); act.sa_flags = SA_SIGINFO; sigaction (SIGALRM, &act, NULL); /* * set up interval timer, starting in three seconds, * then every 1/3 second */ times.it_value.tv_sec = 3; times.it_value.tv_usec = 0; times.it_interval.tv_sec = 0; times.it_interval.tv_usec = 333333; ret = setitimer (ITIMER_REAL, ×, NULL); printf ("main:setitimer ret = %d\n", ret); /* now wait for the alarms */ sigemptyset (&sigset); timerhandler (0, si, NULL); while (timercnt < TIMERCNT) { ret = sigsuspend (&sigset); } printtimes(); } void timerhandler (sig, siginfo, context) int sig; siginfo_t *siginfo; void *context; { printf ("timerhandler:start\n"); gettimeofday (&alarmtimes[timercnt], NULL); timercnt++; printf ("timerhandler:timercnt = %d\n", timercnt); } printtimes () { int i; for (i = 0; i < TIMERCNT; i++) { printf("%ld.%0l6d\n", alarmtimes[i].tv_sec, alarmtimes[i].tv_usec); } }