timer_create()、timer_settime()以及timer_delete
最強大的定時器接口來自POSIX時鍾系列,其創建、初始化以及刪除一個定時器的行動被分為三個不同的函數:timer_create()(創建定時器)、timer_settime()(初始化定時器)以及timer_delete(銷毀它)。
一、創建一個定時器:
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
進程可以通過調用timer_create()創建特定的定時器,定時器是每個進程自己的,不是在fork時繼承的。該函數創建了定時器,並將他的ID 放入timerid指向的位置中。clock_id說明定時器是基於哪個時鍾的,*timerid裝載的是被創建的定時器的ID。evp指定了定時器到期要產生的異步通知。如果evp為NULL,那么定時器到期會產生默認的信號,對 CLOCK_REALTIMER來說,默認信號就是SIGALRM。如果要產生除默認信號之外的其它信號,程序必須將 evp->sigev_signo設置為期望的信號碼。struct sigevent 結構中的 成員evp->sigev_notify說明了定時器到期時應該采取的行動。通常,這個成員的值為SIGEV_SIGNAL,這個值說明在定時器到期時,會產生一個信號。程序可以將成員evp->sigev_notify設為SIGEV_NONE來防止定時器到期時產生信號。如果幾個定時器產生了同一個信號,處理程序可以用 evp->sigev_value來區分是哪個定時器產生了信號。要實現這種功能,程序必須在為信號安裝處理程序時,使用struct sigaction的成員sa_flags中的標志符SA_SIGINFO。
clock_id取值為以下:
CLOCK_REALTIME :Systemwide realtime clock.
CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.
CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.
CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.
CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.
struct sigevent
{
int sigev_notify; //notification type
int sigev_signo; //signal number
union sigval sigev_value; //signal value
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
}
union sigval
{
int sival_int; //integer value
void *sival_ptr; //pointer value
}
通過將evp->sigev_notify設定為如下值來定制定時器到期后的行為:
SIGEV_NONE:什么都不做,只提供通過timer_gettime和timer_getoverrun查詢超時信息。
SIGEV_SIGNAL: 當定時器到期,內核會將sigev_signo所指定的信號傳送給進程。在信號處理程序中,si_value會被設定會sigev_value。
SIGEV_THREAD: 當定時器到期,內核會(在此進程內)以sigev_notification_attributes為線程屬性創建一個線程,並且讓它執行sigev_notify_function,傳入sigev_value作為為一個參數。
sigev_signo的值是SIGALRM以及sigev_value的值是定時器的標識符
二、啟動一個定時器:
timer_create()所創建的定時器並未啟動。要將它關聯到一個到期時間以及啟動時鍾周期,可以使用timer_settime()。
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);
struct itimespec{
struct timespec it_interval;
struct timespec it_value;
};
如同settimer(),it_value用於指定當前的定時器到期時間。當定時器到期,it_value的值會被更新成it_interval 的值。如果it_interval的值為0,則定時器不是一個時間間隔定時器,一旦it_value到期就會回到未啟動狀態。timespec的結構提供了納秒級分辨率:
struct timespec{
time_t tv_sec;
long tv_nsec;
};
如果flags的值為TIMER_ABSTIME,則value所指定的時間值會被解讀成絕對值(此值的默認的解讀方式為相對於當前的時間)。這個經修改的行為可避免取得當前時間、計算“該時間”與“所期望的未來時間”的相對差額以及啟動定時器期間造成競爭條件。
如果ovalue的值不是NULL,則之前的定時器到期時間會被存入其所提供的itimerspec。如果定時器之前處在未啟動狀態,則此結構的成員全都會被設定成0。
三、獲得一個活動定時器的剩余時間:
int timer_gettime(timer_t timerid,struct itimerspec *value);
取得一個定時器的超限運行次數:
有可能一個定時器到期了,而同一定時器上一次到期時產生的信號還處於掛起狀態。在這種情況下,其中的一個信號可能會丟失。這就是定時器超限。程序可以通過調 用timer_getoverrun來確定一個特定的定時器出現這種超限的次數。定時器超限只能發生在同一個定時器產生的信號上。由多個定時器,甚至是那 些使用相同的時鍾和信號的定時器,所產生的信號都會排隊而不會丟失。
int timer_getoverrun(timer_t timerid);
執行成功時,timer_getoverrun()會返回定時器初次到期與通知進程(例如通過信號)定時器已到期之間額外發生的定時器到期次數。舉例來說,在我們之前的例子中,一個1ms的定時器運行了10ms,則此調用會返回9。如果超限運行的次數等於或大於DELAYTIMER_MAX,則此調用會返回DELAYTIMER_MAX。
執行失敗時,此函數會返回-1並將errno設定會EINVAL,這個唯一的錯誤情況代表timerid指定了無效的定時器。
四、刪除一個定時器:
int timer_delete (timer_t timerid);
一次成功的timer_delete()調用會銷毀關聯到timerid的定時器並且返回0。執行失敗時,此調用會返回-1並將errno設定會 EINVAL,這個唯一的錯誤情況代表timerid不是一個有效的定時器。
例1:
void handle()
{
time_t t;
char p[32];
time(&t);
strftime(p, sizeof(p), "%T", localtime(&t));
printf("time is %s/n", p);
}
int main()
{
struct sigevent evp;
struct itimerspec ts;
timer_t timer;
int ret;
evp.sigev_value.sival_ptr = &timer;
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR1;
signal(SIGUSR1, handle);
ret = timer_create(CLOCK_REALTIME, &evp, &timer);
if( ret )
perror("timer_create");
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
ts.it_value.tv_sec = 3;
ts.it_value.tv_nsec = 0;
ret = timer_settime(timer, 0, &ts, NULL);
if( ret )
perror("timer_settime");
while(1);
}
例2:
void handle(union sigval v)
{
time_t t;
char p[32];
time(&t);
strftime(p, sizeof(p), "%T", localtime(&t));
printf("%s thread %lu, val = %d, signal captured./n", p, pthread_self(), v.sival_int);
return;
}
int main()
{
struct sigevent evp;
struct itimerspec ts;
timer_t timer;
int ret;
memset (&evp, 0, sizeof (evp));
evp.sigev_value.sival_ptr = &timer;
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_notify_function = handle;
evp.sigev_value.sival_int = 3; //作為handle()的參數
ret = timer_create(CLOCK_REALTIME, &evp, &timer);
if( ret)
perror("timer_create");
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
ts.it_value.tv_sec = 3;
ts.it_value.tv_nsec = 0;
ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);
if( ret )
perror("timer_settime");
while(1);
}
五、Linux 下取消用setitimer設置的定時器
Setitimer設置it_interval和it_value為零:
void uninit_time()
{
struct itimerval value;
value.it_value.tv_sec = 0;
value.it_value.tv_usec = 0;
value.it_interval = value.it_value;
setitimer(ITIMER_REAL, &value, NULL);
}