定时器timerfd
1.為什么要加入此定時(shí)器接口
linux2.6.25版本新增了timerfd這個(gè)供用戶(hù)程序使用的定時(shí)接口,這個(gè)接口基于文件描述符,當(dāng)超時(shí)事件發(fā)生時(shí),該文件描述符就變?yōu)榭勺x。我首次接觸這個(gè)新特性是在muduo網(wǎng)絡(luò)庫(kù)的定時(shí)器里看到的,那么新增一個(gè)這樣的定時(shí)器接口有什么意義呢?
要說(shuō)明這個(gè)問(wèn)題我得先給大家列舉一下Linux下能實(shí)現(xiàn)定時(shí)功能的各個(gè)接口,然后通過(guò)逐一比較來(lái)說(shuō)明原因
linux下的定時(shí)接口主要有如下幾種
.sleep()
.alarm()
.usleep()
.nanosleep()
.clock_nanosleep()
.getitimer()/setitimer()
.timer_create()/timer_settime/timer_gettime()/timer_delete()
.timerfd_create()/timerfd_gettime()/timer_settime()
以上便是Linux下常用的一些定時(shí)接口?
1.前三種sleep()/alarm()/usleep()在實(shí)現(xiàn)時(shí)可能用了SIGALRM信號(hào),在多線程中使用信號(hào)是相當(dāng)麻煩的?
2.nanosleep()/clock_nanosleep()會(huì)讓線程掛起,這樣會(huì)使程序失去響應(yīng),多線程網(wǎng)絡(luò)編程中我們應(yīng)該避免這樣做?
3.getitimer()/timer_cteate()也是用信號(hào)來(lái)deliver超時(shí)
而我們的timerfd_create()把時(shí)間變成了一個(gè)文件描述符,該文件描述符會(huì)在超時(shí)時(shí)變得可讀,這種特性可以使我們?cè)趯?xiě)服務(wù)器程序時(shí),很方便的便把定時(shí)事件變成和其他I/O事件一樣的處理方式,并且此定時(shí)接口的精度也足夠的高,所以我們只要以后在寫(xiě)I/O框架時(shí)用到了定時(shí)器就該首選timerfd_create()
2.timerfd的接口介紹
(1)timerfd的創(chuàng)建
int timer_create(int clockid,int flags);
//成功返回0
?
第一個(gè)參數(shù)一般為CLOCK_REALTIME或者CLOCK_MONOTONIC,其參數(shù)意義為參數(shù)意義
CLOCK_REALTIME:相對(duì)時(shí)間,從1970.1.1到目前時(shí)間,之所以說(shuō)其為相對(duì)時(shí)間,是因?yàn)槲覀冎灰淖儺?dāng)前系統(tǒng)的時(shí)間,從1970.1.1到當(dāng)前時(shí)間就會(huì)發(fā)生變化,所以說(shuō)其為相對(duì)時(shí)間
CLOCK_MONOTONIC:與CLOCK_REALTIME相反,它是以絕對(duì)時(shí)間為準(zhǔn),獲取的時(shí)間為系統(tǒng)最近一次重啟到現(xiàn)在的時(shí)間,更該系統(tǒng)時(shí)間對(duì)其沒(méi)影響
第二個(gè)參數(shù)為控制標(biāo)志:TFD_NONBLOCK(非阻塞),TFD_CLOEXEC(同O_CLOEXEC)
(2)定時(shí)器的設(shè)置
int timerfd_settime(int fd,int flags
const struct itimerspec *new_value
struct itimerspec *old_value);
//成功返回0
?
該函數(shù)的功能為啟動(dòng)和停止定時(shí)器,第一個(gè)參數(shù)fd為上面的timerfd_create()函數(shù)返回的定時(shí)器文件描述符,第二個(gè)參數(shù)flags為0表示相對(duì)定時(shí)器,為T(mén)FD_TIMER_ABSTIME表示絕對(duì)定時(shí)器,第三個(gè)參數(shù)new_value用來(lái)設(shè)置超時(shí)時(shí)間,為0表示停止定時(shí)器,第四個(gè)參數(shù)為原來(lái)的超時(shí)時(shí)間,一般設(shè)為NULL
需要注意的是我們可以通過(guò)clock_gettime獲取當(dāng)前時(shí)間,如果是絕對(duì)定時(shí)器,那么我們得獲取1970.1.1到當(dāng)前時(shí)間(CLOCK_REALTIME),在加上我們自己定的定時(shí)時(shí)間。若是相對(duì)定時(shí),則要獲取我們系統(tǒng)本次開(kāi)機(jī)到目前的時(shí)間加我們要定的時(shí)常(即獲取CLOCK_MONOTONIC時(shí)間)
上述參數(shù)中itimerspec的結(jié)構(gòu)定義如下
struct itimerspec {
struct timespec it_interval; /* Interval for periodic timer */
struct timespec it_value; /* Initial expiration */
};
?
其中it_value保存首次超時(shí)時(shí)間值,即在哪個(gè)時(shí)間點(diǎn)超時(shí)的那個(gè)時(shí)間的值,it_interval為后續(xù)周期性超時(shí)的時(shí)間間隔,注意是時(shí)間間隔不是時(shí)間值啦?
timespec的結(jié)構(gòu)定義如下
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
};
?
需要注意的是當(dāng)設(shè)置定時(shí)器后,我們就可以用read讀取定時(shí)器的文件描述符了,當(dāng)其可讀時(shí),就是超時(shí)發(fā)生的時(shí)間,下面的實(shí)例中給出用法,請(qǐng)讀者仔細(xì)體會(huì)
3.具體實(shí)例
以絕對(duì)超時(shí)為例
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> /* Definition of uint64_t */
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
//打印當(dāng)前定時(shí)距首次開(kāi)始計(jì)時(shí)的時(shí)間
static void print_elapsed_time(void)
{
static struct timespec start;
struct timespec curr;
static int first_call = 1;
int secs, nsecs;
if (first_call) { //獲取開(kāi)始時(shí)間
first_call = 0;
if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
handle_error("clock_gettime");
}
if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
handle_error("clock_gettime");
//時(shí)間差等于每次的當(dāng)前時(shí)間減去start的開(kāi)始時(shí)間
secs = curr.tv_sec - start.tv_sec;
nsecs = curr.tv_nsec - start.tv_nsec;
if (nsecs < 0) {
secs--;
nsecs += 1000000000; //相差的納秒數(shù)
}
printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}
int main(int argc, char *argv[])
{
struct itimerspec new_value;
int max_exp, fd;
struct timespec now;
uint64_t exp, tot_exp;
ssize_t s;
if ((argc != 2) && (argc != 4)) {
fprintf(stderr, "%s init-secs [interval-secs max-exp]\n",
argv[0]);
exit(EXIT_FAILURE);
}
if (clock_gettime(CLOCK_REALTIME, &now) == -1)
handle_error("clock_gettime");
/* Create a CLOCK_REALTIME absolute timer with initial
expiration and interval as specified in command line */
new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
new_value.it_value.tv_nsec = now.tv_nsec;
if (argc == 2) {
new_value.it_interval.tv_sec = 0;
max_exp = 1;
} else {
new_value.it_interval.tv_sec = atoi(argv[2]); //之后的定時(shí)間隔
max_exp = atoi(argv[3]); //定時(shí)總次數(shù)
}
new_value.it_interval.tv_nsec = 0;
//生成與定時(shí)器關(guān)聯(lián)的文件描述符
fd = timerfd_create(CLOCK_REALTIME, 0);
if (fd == -1)
handle_error("timerfd_create");
if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
handle_error("timerfd_settime");
//獲取并打印首次首次定時(shí)開(kāi)始的時(shí)間
print_elapsed_time();
printf("timer started\n");
for (tot_exp = 0; tot_exp < max_exp;) {
s = read(fd, &exp, sizeof(uint64_t)); //read阻塞等待知道超時(shí)發(fā)生
if (s != sizeof(uint64_t))
handle_error("read");
tot_exp += exp;
print_elapsed_time();
printf("read: %llu; total=%llu\n",
(unsigned long long) exp,
(unsigned long long) tot_exp);
}
exit(EXIT_SUCCESS);
}
?
?
總結(jié)
以上是生活随笔為你收集整理的定时器timerfd的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 哈利波特手游巫师棋怎么玩
- 下一篇: timerfd与epoll