Linux C/C++定時器的實現(xiàn)原理和使用方法

在實際項目中磨隘,一個常用的做法是新起一個線程,專門管理定時器蹋订,定時來源使用rtc率挣、select等比較精確的來源,定時器超時后向主要的work線程發(fā)消息即可露戒,或者使用timefd接口椒功。

定時器的實現(xiàn)原理

定時器的實現(xiàn)依賴的是CPU時鐘中斷,時鐘中斷的精度就決定定時器精度的極限智什。一個時鐘中斷源如何實現(xiàn)多個定時器呢动漾?對于內核,簡單來說就是用特定的數(shù)據(jù)結構管理眾多的定時器荠锭,在時鐘中斷處理中判斷哪些定時器超時旱眯,然后執(zhí)行超時處理動作。而用戶空間程序不直接感知CPU時鐘中斷,通過感知內核的信號删豺、IO事件共虑、調度,間接依賴時鐘中斷呀页。用軟件來實現(xiàn)動態(tài)定時器常用數(shù)據(jù)結構有:時間輪妈拌、最小堆和紅黑樹。下面就是一些知名的實現(xiàn):

Linux內核的 Hierarchy 時間輪算法

Asio C++ Library最小堆定時器實現(xiàn)

nginx 使用紅黑樹結構管理定時器事件

內核定時器時間輪算法

單層時間輪算法的原理比較簡單:用一個數(shù)組表示時間輪蓬蝶,每個時鐘周期尘分,時間輪 current 往后走一個格,并處理掛在這個格子的定時器鏈表丸氛,如果超時則進行超時動作處理培愁,然后刪除定時器,沒有則剩余輪數(shù)減一缓窜。原理如圖:

Linux 內核則采用的是 Hierarchy 時間輪算法定续,Hierarchy 時間輪將單一的 bucket 數(shù)組分成了幾個不同的數(shù)組,每個數(shù)組表示不同的時間精度禾锤,Linux 內核中用 jiffies 記錄時間香罐,jiffies記錄了系統(tǒng)啟動以來經(jīng)過了多少tick。

Hierarchy 時間輪的原理大致如下时肿,下面是一個時分秒的Hierarchy時間輪,不同于Linux內核的實現(xiàn)港粱,但原理類似螃成。對于時分秒三級時間輪,每個時間輪都維護一個cursor查坪,新建一個timer時寸宏,要掛在合適的格子,剩余輪數(shù)以及時間都要記錄偿曙,到期判斷超時并調整位置氮凝。原理圖大致如下:


定時器的使用方法

在Linux 用戶空間程序開發(fā)中,常用的定期器可以分為兩類:

執(zhí)行一次的單次定時器 single-short望忆;

循環(huán)執(zhí)行的周期定時器 Repeating Timer罩阵;

其中,Repeating Timer 可以通過在Single-Shot Timer 終止之后启摄,重新再注冊到定時器系統(tǒng)里來實現(xiàn)稿壁。當一個進程需要使用大量定時器時,同樣利用時間輪歉备、最小堆或紅黑樹等結構來管理定時器傅是。而時鐘周期來源則需要借助系統(tǒng)調用,最終還是從時鐘中斷。Linux用戶空間程序的定時器可用下面方法來實現(xiàn):

通過alarm()或setitimer()系統(tǒng)調用喧笔,非阻塞異步帽驯,配合SIGALRM信號處理;

通過select()或nanosleep()系統(tǒng)調用书闸,阻塞調用尼变,往往需要新建一個線程;

通過timefd()調用梗劫,基于文件描述符享甸,可以被用于 select/poll 的應用場景;

通過RTC機制, 利用系統(tǒng)硬件提供的Real Time Clock機制, 計時非常精確梳侨;

上面方法沒提sleep()蛉威,因為Linux中并沒有系統(tǒng)調用sleep(),sleep()是在庫函數(shù)中實現(xiàn)走哺,是通過調用alarm()來設定報警時間蚯嫌,調用sigsuspend()將進程掛起在信號SIGALARM上,而且sleep()也只能精確到秒級上丙躏,精度不行择示。當使用阻塞調用作為定時周期來源時,可以單獨啟一個線程用來管理所有定時器晒旅,當定時器超時的時候栅盲,向業(yè)務線程發(fā)送定時器消息即可。

一個基于時間輪的定時器簡單實現(xiàn)

#include <stdio.h>

#include <signal.h>

#include <stdlib.h>

#include <unistd.h>

#define TIME_WHEEL_SIZE 8

typedef void (*func)(int data);

struct timer_node {

struct timer_node *next;

int rotation;

func proc;

int data;

};

struct timer_wheel {

struct timer_node *slot[TIME_WHEEL_SIZE];

int current;

};

struct timer_wheel timer = {{0}, 0};

void tick(int signo)

{

// 使用二級指針刪進行單鏈表的刪除

struct timer_node **cur = &timer.slot[timer.current];

while (*cur) {

struct timer_node *curr = *cur;

if (curr->rotation > 0) {

curr->rotation--;

cur = &curr->next;

} else {

curr->proc(curr->data);

*cur = curr->next;

free(curr);

}

}

timer.current = (timer.current + 1) % TIME_WHEEL_SIZE;

alarm(1);

}

void add_timer(int len, func action)

{

int pos = (len + timer.current) % TIME_WHEEL_SIZE;

struct timer_node *node = malloc(sizeof(struct timer_node));

// 插入到對應格子的鏈表頭部即可, O(1)復雜度

node->next = timer.slot[pos];

timer.slot[pos] = node;

node->rotation = len / TIME_WHEEL_SIZE;

node->data = 0;

node->proc = action;

}

// test case1: 1s循環(huán)定時器

int g_sec = 0;

void do_time1(int data)

{

printf("timer %s, %d\n", __FUNCTION__, g_sec++);

add_timer(1, do_time1);

}

// test case2: 2s單次定時器

void do_time2(int data)

{

printf("timer %s\n", __FUNCTION__);

}

// test case3: 9s循環(huán)定時器

void do_time9(int data)

{

printf("timer %s\n", __FUNCTION__);

add_timer(9, do_time9);

}

int main()

{

signal(SIGALRM, tick);

alarm(1); // 1s的周期心跳

// test

add_timer(1, do_time1);

add_timer(2, do_time2);

add_timer(9, do_time9);

while(1) pause();

return 0;

}

轉載

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末废恋,一起剝皮案震驚了整個濱河市谈秫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鱼鼓,老刑警劉巖拟烫,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迄本,居然都是意外死亡硕淑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門嘉赎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來置媳,“玉大人,你說我怎么就攤上這事公条“肟” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵赃份,是天一觀的道長寂拆。 經(jīng)常有香客問我奢米,道長,這世上最難降的妖魔是什么纠永? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任鬓长,我火速辦了婚禮,結果婚禮上尝江,老公的妹妹穿的比我還像新娘涉波。我一直安慰自己,他們只是感情好炭序,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布啤覆。 她就那樣靜靜地躺著,像睡著了一般惭聂。 火紅的嫁衣襯著肌膚如雪窗声。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天辜纲,我揣著相機與錄音笨觅,去河邊找鬼。 笑死耕腾,一個胖子當著我的面吹牛见剩,可吹牛的內容都是我干的。 我是一名探鬼主播扫俺,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼苍苞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狼纬?” 一聲冷哼從身側響起柒啤,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎畸颅,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體方援,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡没炒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了犯戏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片送火。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖先匪,靈堂內的尸體忽然破棺而出种吸,到底是詐尸還是另有隱情,我是刑警寧澤呀非,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布坚俗,位于F島的核電站镜盯,受9級特大地震影響,放射性物質發(fā)生泄漏猖败。R本人自食惡果不足惜速缆,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恩闻。 院中可真熱鬧艺糜,春花似錦、人聲如沸幢尚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尉剩。三九已至真慢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間边涕,已是汗流浹背晤碘。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留功蜓,地道東北人园爷。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像式撼,于是被迫代替她去往敵國和親童社。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內容

  • 在上一節(jié)已經(jīng)了解到Linux上使用各種定時器的優(yōu)缺點著隆,接下來主要介紹Posix定時器扰楼。傳送門:關于Linux應用層...
    JalynFang閱讀 9,625評論 0 4
  • 進程 創(chuàng)建 創(chuàng)建進程用fork()函數(shù)。fork()為子進程創(chuàng)建新的地址空間并且拷貝頁表美浦。子進程的虛擬地址空間...
    梅花怒閱讀 1,914評論 0 7
  • 1弦赖、綜述 基于硬件工程師的提出的一個測試需求:每隔5秒鐘拉高PA的使能腳,間隔5秒鐘再拉低PA使能腳(這里的PA ...
    c楓_擼碼的日子閱讀 14,944評論 0 10
  • C語言是面向過程的浦辨,而C++是面向對象的 C和C++的區(qū)別: C是一個結構化語言蹬竖,它的重點在于算法和數(shù)據(jù)結構。C程...
    小辰帶你看世界閱讀 5,515評論 0 3
  • 簡書上幾個朋友的旅游日志又把我撩的睡不著了流酬! 如果當初沒有接觸到左總币厕,恐怕我現(xiàn)在都不會想著...
    讀書吃飯閱讀 493評論 0 3