timerfd
1.為什么要加入此定時器接口
linux2.6.25版本新增了timerfd這個供用戶程序使用的定時接口,這個接口基于文件描述符,當超時事件發生時,該文件描述符就變為可讀。我首次接觸這個新特性是在muduo網絡庫的定時器里看到的,那么新增一個這樣的定時器接口有什么意義呢?
要說明這個問題我得先給大家列舉一下Linux下能實現定時功能的各個接口,然后通過逐一比較來說明原因
linux下的定時接口主要有如下幾種
.sleep() .alarm() .usleep() .nanosleep() .clock_nanosleep() .getitimer()/setitimer() .timer_create()/timer_settime/timer_gettime()/timer_delete() .timerfd_create()/timerfd_gettime()/timer_settime() 以上便是Linux下常用的一些定時接口?
1.前三種sleep()/alarm()/usleep()在實現時可能用了SIGALRM信號,在多線程中使用信號是相當麻煩的?
2.nanosleep()/clock_nanosleep()會讓線程掛起,這樣會使程序失去響應,多線程網絡編程中我們應該避免這樣做?
3.getitimer()/timer_cteate()也是用信號來deliver超時
而我們的timerfd_create()把時間變成了一個文件描述符,該文件描述符會在超時時變得可讀,這種特性可以使我們在寫服務器程序時,很方便的便把定時事件變成和其他I/O事件一樣的處理方式,并且此定時接口的精度也足夠的高,所以我們只要以后在寫I/O框架時用到了定時器就該首選timerfd_create()
2.timerfd的接口介紹
(1)timerfd的創建
int timer_create(int clockid,int flags);//成功返回0- 1
- 2
- 3
- 1
- 2
- 3
第一個參數一般為CLOCK_REALTIME或者CLOCK_MONOTONIC,其參數意義為參數意義
CLOCK_REALTIME:相對時間,從1970.1.1到目前時間,之所以說其為相對時間,是因為我們只要改變當前系統的時間,從1970.1.1到當前時間就會發生變化,所以說其為相對時間
CLOCK_MONOTONIC:與CLOCK_REALTIME相反,它是以絕對時間為準,獲取的時間為系統最近一次重啟到現在的時間,更該系統時間對其沒影響
第二個參數為控制標志:TFD_NONBLOCK(非阻塞),TFD_CLOEXEC(同O_CLOEXEC)
(2)定時器的設置
int timerfd_settime(int fd,int flagsconst struct itimerspec *new_valuestruct itimerspec *old_value);//成功返回0- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
該函數的功能為啟動和停止定時器,第一個參數fd為上面的timerfd_create()函數返回的定時器文件描述符,第二個參數flags為0表示相對定時器,為TFD_TIMER_ABSTIME表示絕對定時器,第三個參數new_value用來設置超時時間,為0表示停止定時器,第四個參數為原來的超時時間,一般設為NULL
需要注意的是我們可以通過clock_gettime獲取當前時間,如果是絕對定時器,那么我們得獲取1970.1.1到當前時間(CLOCK_REALTIME),在加上我們自己定的定時時間。若是相對定時,則要獲取我們系統本次開機到目前的時間加我們要定的時常(即獲取CLOCK_MONOTONIC時間)
上述參數中itimerspec的結構定義如下
struct itimerspec {struct timespec it_interval; /* Interval for periodic timer */struct timespec it_value; /* Initial expiration */};- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
其中it_value保存首次超時時間值,即在哪個時間點超時的那個時間的值,it_interval為后續周期性超時的時間間隔,注意是時間間隔不是時間值啦?
timespec的結構定義如下
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
需要注意的是當設置定時器后,我們就可以用read讀取定時器的文件描述符了,當其可讀時,就是超時發生的時間,下面的實例中給出用法,請讀者仔細體會
3.具體實例
以絕對超時為例
#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)//打印當前定時距首次開始計時的時間 static void print_elapsed_time(void){static struct timespec start;struct timespec curr;static int first_call = 1;int secs, nsecs;if (first_call) { //獲取開始時間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");//時間差等于每次的當前時間減去start的開始時間secs = curr.tv_sec - start.tv_sec;nsecs = curr.tv_nsec - start.tv_nsec;if (nsecs < 0) {secs--;nsecs += 1000000000; //相差的納秒數}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 initialexpiration 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]); //之后的定時間隔max_exp = atoi(argv[3]); //定時總次數}new_value.it_interval.tv_nsec = 0;//生成與定時器關聯的文件描述符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");//獲取并打印首次首次定時開始的時間print_elapsed_time();printf("timer started\n");for (tot_exp = 0; tot_exp < max_exp;) {s = read(fd, &exp, sizeof(uint64_t)); //read阻塞等待知道超時發生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);}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
編譯并運行上述程序,結果如下?
當定一次值,設為3秒后超時?
當定5次時,初次為3s后之后每1秒超時一次,運行結果如下?
總結
- 上一篇: 在这里的周末休息也就是看看奥运
- 下一篇: 用 CSS 做轮播图