1. 程式人生 > >linux定時器總結

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 的時候使用到,應用程式很少會需要用到這種模式。