linux定時器總結
1 參考資料
Ø 《linux系統程式設計》第“10.9 定時器”章節
2 概要
要在linux中使用定時器,有如下三種方法:
定時器方式 |
一個程序允許 使用的數量 |
通知方式 |
簡單的鬧鐘 - alarm |
1個 |
Ø 訊號: SIGALRM |
間歇定時器 - setitimer |
1個 |
Ø 訊號: SIGALRM、SIGVTALRM、SIGPROF |
高階定時器 - timer_create |
無限制 |
Ø 訊號: 可以自己選擇用什麼訊號 Ø 啟動執行緒 就不會產生訊號,可以避免慢系統呼叫被訊號中斷的問題 |
3 alarm
alarm()是最簡單的定時器介面,對該函式的呼叫會在真實時間(real time)seconds秒之後將SIGALRM訊號發給呼叫程序。它不能自動重啟(即定時器到後要再次定時,需要再呼叫一次alarm)。
4 setitimer
間歇定時器setitimer系統呼叫,他可以提供比alarm()更多的控制。它能夠自動重啟。
linux 為每一個程序提供了 3 個 setitimer 間隔計時器:
Ø ITIMER_REAL:減少實際時間,到期的時候發出 SIGALRM 訊號。
Ø ITIMER_VIRTUAL:減少有效時間 (程序執行的時間),產生 SIGVTALRM 訊號。
Ø ITIMER_PROF:減少程序的有效時間和系統時間 (為程序排程用的時間)。這個經常和上面一個使用用來計算系統核心時間和使用者時間。產生 SIGPROF 訊號。
所謂 REAL 時間,即我們人類自然感受的時間,英文計算機文件中也經常使用 wall-clock 這個術語。說白了就是我們通常所說的時間,比如現在是下午 5 點 10 分,那麼一分鐘的 REAL 時間之後就是下午 5 點 11 分。
VIRTUAL 時間是程序執行的時間,Linux 是一個多使用者多工系統,在過去的 1 分鐘內,指定程序實際在 CPU 上的執行時間往往並沒有 1 分鐘,因為其他程序會被 Linux 排程執行,在那些時間內,雖然自然時間在流逝,但指定程序並沒有真正的執行。VIRTUAL 時間就是指定程序真正的有效執行時間。比如 5 點 10 分開始的 1 分鐘內,程序 P1 被 Linux 排程並佔用 CPU 的執行時間為 30 秒,那麼 VIRTUAL 時間對於程序 P1 來講就是 30 秒。此時自然時間已經到了 5 點 11 分,但從程序 P1 的眼中看來,時間只過了 30 秒。
PROF 時間比較獨特,對程序 P1 來說從 5 點 10 分開始的 1 分鐘內,雖然自己的執行時間為 30 秒,但實際上還有 10 秒鐘核心是在執行 P1 發起的系統呼叫,那麼這 10 秒鐘也被加入到 PROF 時間。這種時間定義主要用於全面衡量程序的效能,因為在統計程式效能的時候,10 秒的系統呼叫時間也應該算到 P1 的頭上。這也許就是 PROF 這個名字的來歷吧。
5 POSIX Timer
間隔定時器 setitimer 有一些重要的缺點,POSIX Timer 對 setitimer 進行了增強,克服了 setitimer 的諸多問題:
Ø 首先,一個程序同一時刻只能有一個 timer。假如應用需要同時維護多個 Interval 不同的計時器,必須自己寫程式碼來維護。這非常不方便。使用 POSIX Timer,一個程序可以建立任意多個 Timer。
Ø setitmer 計時器時間到達時,只能使用訊號方式通知使用 timer 的程序,而 POSIX timer 可以有多種通知方式,比如訊號,或者啟動執行緒。
Ø 使用 setitimer 時,通知訊號的類別不能改變:SIGALARM,SIGPROF 等,而這些都是傳統訊號,而不是實時訊號,因此有 timer overrun 的問題;而 POSIX Timer 則可以使用實時訊號。
備註:通過kill –l可以檢視系統支援的所有訊號列表。編號為1 ~ 31的訊號為傳統UNIX支援的訊號,是不可靠訊號(非實時的),編號為32 ~ 63的訊號是後來擴充的,稱做可靠訊號(實時訊號)。不可靠訊號和可靠訊號的區別在於前者不支援排隊,可能會造成訊號丟失,而後者不會。
Ø setimer 的精度是 ms,POSIX Timer 是針對有實時要求的應用所設計的,介面支援 ns 級別的時鐘精度。
表 2. POSIX Timer 函式
函式名 |
功能描述 |
timer_create |
建立一個新的 Timer;並且指定定時器到時通知機制 |
timer_delete |
刪除一個 Timer |
timer_gettime |
Get the time remaining on a POSIX.1b interval timer |
timer_settime |
開始或者停止某個定時器。 |
timer_getoverrun |
獲取丟失的定時通知個數。 |
使用 Posix Timer 的基本流程很簡單,首先建立一個 Timer。建立的時候可以指定該 Timer 的一些特性,比如 clock ID。clock ID 即 Timer 的種類,可以為下表中的任意一種:
表 3. POSIX Timer clock ID
Clock ID |
描述 |
CLOCK_REALTIME |
Settable system-wide real-time clock; |
CLOCK_MONOTONIC |
Nonsettable monotonic clock |
CLOCK_PROCESS_CPUTIME_ID |
Per-process CPU-time clock |
CLOCK_THREAD_CPUTIME_ID |
Per-thread CPU-time clock |
Ø CLOCK_REALTIME 時間是系統儲存的時間,即可以由 date 命令顯示的時間,該時間可以重新設定。比如當前時間為上午 10 點 10 分,Timer 打算在 10 分鐘後到時。假如 5 分鐘後,我用 date 命令修改當前時間為 10 點 10 分,那麼 Timer 還會再等十分鐘到期,因此實際上 Timer 等待了 15 分鐘。假如您希望無論任何人如何修改系統時間,Timer 都嚴格按照 10 分鐘的週期進行觸發,那麼就可以使用CLOCK_MONOTONIC。
Ø CLOCK_PROCESS_CPUTIME_ID 的含義與 setitimer 的 ITIMER_VIRTUAL 類似。計時器只記錄當前程序所實際花費的時間;比如還是上面的例子,假設系統非常繁忙,當前程序只能獲得 50%的 CPU 時間,為了讓程序真正地執行 10 分鐘,應該到 10 點 30 分才允許 Timer 到期。
Ø CLOCK_THREAD_CPUTIME_ID 以執行緒為計時實體,當前程序中的某個執行緒真正地運行了一定時間才觸發 Timer。
設定到期通知方式
timer_create 的第二個引數 struct sigevent 用來設定定時器到時時的通知方式。該資料結構如下:
struct sigevent {
int sigev_notify; /* Notification method */
int sigev_signo; /* Notification signal */
union sigval sigev_value; /* Data passed with
notification */
void (*sigev_notify_function) (union sigval);
/* Function used for thread
notification (SIGEV_THREAD) */
void *sigev_notify_attributes;
/* Attributes for notification thread
(SIGEV_THREAD) */
pid_t sigev_notify_thread_id;
/* ID of thread to signal (SIGEV_THREAD_ID) */
};
其中sigev_notify 表示通知方式,有如下幾種:
表 3. POSIX Timer 到期通知方式
通知方式 |
描述 |
SIGEV_NONE |
定時器到期時不產生通知。。。 |
SIGEV_SIGNAL |
定時器到期時將給程序投遞一個訊號,sigev_signo 可以用來指定使用什麼訊號。 |
SIGEV_THREAD |
定時器到期時將啟動新的執行緒進行需要的處理 |
SIGEV_THREAD_ID(僅針對Linux) |
定時器到期時將向指定執行緒傳送訊號。 |
Ø 如果採用 SIGEV_NONE 方式,使用者必須呼叫timer_gettime 函式主動讀取定時器已經走過的時間。類似輪詢。
Ø 如果採用 SIGEV_SIGNAL 方式,使用者可以選擇使用什麼訊號,用 sigev_signo 表示訊號值,比如 SIG_ALARM。
Ø 如果使用 SIGEV_THREAD 方式,則需要設定 sigev_notify_function,當 Timer 到期時,將使用該函式作為入口啟動一個執行緒來處理訊號;sigev_value 儲存了傳入 sigev_notify_function 的引數。sigev_notify_attributes 如果非空,則應該是一個指向 pthread_attr_t 的指標,用來設定執行緒的屬性(比如 stack 大小,detach 狀態等)。
Ø SIGEV_THREAD_ID 通常和 SIGEV_SIGNAL 聯合使用,這樣當 Timer 到期時,系統會向由 sigev_notify_thread_id 指定的執行緒傳送訊號,否則可能程序中的任意執行緒都可能收到該訊號。這個選項是 Linux 對 POSIX 標準的擴充套件,目前主要是 GLibc 在實現 SIGEV_THREAD 的時候使用到,應用程式很少會需要用到這種模式。