1. 程式人生 > >Linux下定時器的實現方式分析

Linux下定時器的實現方式分析

級別: 初級

趙 軍 ([email protected]), 開發工程師, Pixelworks

2009 年 10 月 31 日

定時器屬於基本的基礎元件,不管是使用者空間的程式開發,還是核心空間的程式開發,很多時候都需要有定時器作為基礎元件的支援,但使用場景的不同,對定時器的實現考慮也不盡相同,本文討論了在 Linux 環境下,應用層和核心層的定時器的各種實現方法,並分析了各種實現方法的利弊以及適宜的使用環境。

概論

定時器屬於基本的基礎元件,不管是使用者空間的程式開發,還是核心空間的程式開發,很多時候都需要有定時器作為基礎元件的支援,但使用場景的不同,對定時器的實現考慮也不盡相同,本文討論了在 Linux 環境下,應用層和核心層的定時器的各種實現方法,並分析了各種實現方法的利弊以及適宜的使用環境。

首先,給出一個基本模型,定時器的實現,需要具備以下幾個行為,這也是在後面評判各種定時器實現的一個基本模型 [1]:

StartTimer(Interval, TimerId, ExpiryAction)

註冊一個時間間隔為 Interval 後執行 ExpiryAction 的定時器例項,其中,返回 TimerId 以區分在定時器系統中的其他定時器例項。

StopTimer(TimerId)

根據 TimerId 找到註冊的定時器例項並執行 Stop 。

PerTickBookkeeping()

在一個 Tick 內,定時器系統需要執行的動作,它最主要的行為,就是檢查定時器系統中,是否有定時器例項已經到期。注意,這裡的 Tick 實際上已經隱含了一個時間粒度 (granularity) 的概念。

ExpiryProcessing()

在定時器例項到期之後,執行預先註冊好的 ExpiryAction 行為。

上面說了基本的定時器模型,但是針對實際的使用情況,又有以下 2 種基本行為的定時器:

Single-Shot Timer

這種定時器,從註冊到終止,僅僅只執行一次。

Repeating Timer

這種定時器,在每次終止之後,會自動重新開始。本質上,可以認為 Repeating Timer 是在 Single-Shot Timer 終止之後,再次註冊到定時器系統裡的 Single-Shot Timer,因此,在支援 Single-Shot Timer 的基礎上支援 Repeating Timer 並不算特別的複雜。






基於連結串列和訊號實現定時器 (2.4 版核心情況下 )

在 2.4 的核心中,並沒有提供 POSIX timer [ 2 ]的支援,要在程序環境中支援多個定時器,只能自己來實現,好在 Linux 提供了 setitimer(2) 的介面。它是一個具有間隔功能的定時器 (interval timer),但如果想在程序環境中支援多個計時器,不得不自己來管理所有的計時器。 setitimer(2) 的定義如下:


清單 1. setitimer 的原型
#include <sys/time.h> 

 int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);

setitimer 能夠在 Timer 到期之後,自動再次啟動自己,因此,用它來解決 Single-Shot Timer 和 Repeating Timer 的問題顯得很簡單。該函式可以工作於 3 種模式:

ITIMER_REAL 以實時時間 (real time) 遞減,在到期之後傳送 SIGALRM 訊號

ITIMER_VIRTUAL 僅程序在使用者空間執行時遞減,在到期之後傳送 SIGVTALRM 訊號

ITIMER_PROF 程序在使用者空間執行以及核心為該程序服務時 ( 典型如完成一個系統呼叫 ) 都會遞減,與 ITIMER_VIRTUAL 共用時可度量該應用在核心空間和使用者空間的時間消耗情況,在到期之後傳送 SIGPROF 訊號

定時器的值由下面的結構定義:


清單 2. setitimer 定時器的值定義
struct itimerval { 
 struct timeval it_interval; /* next value */ 
 struct timeval it_value;     /* current value */ 
 }; 

 struct timeval { 
        long tv_sec;                /* seconds */ 
        long tv_usec;               /* microseconds */ 
 };

setitimer() 以 new_value 設定特定的定時器,如果 old_value 非空,則它返回 which 型別時間間隔定時器的前一個值。定時器從 it_value 遞減到零,然後產生一個訊號,並重新設定為 it_interval,如果此時 it_interval 為零,則該定時器停止。任何時候,只要 it_value 設定為零,該定時器就會停止。

由於 setitimer() 不支援在同一程序中同時使用多次以支援多個定時器,因此,如果需要同時支援多個定時例項的話,需要由實現者來管理所有的例項。用 setitimer() 和連結串列,可以構造一個在程序環境下支援多個定時器例項的 Timer,在一般的實現中的 PerTickBookkeeping 時,會遞增每個定時器的 elapse 值,直到該值遞增到最初設定的 interval 則表示定時器到期。

基於連結串列實現的定時器可以定義為:


清單 3. 基於連結串列的定時器定義
typedef int timer_id; 

 /** 
 * The type of callback function to be called by timer scheduler when a timer 
 * has expired. 
 * 
 * @param id                The timer id. 
 * @param user_data        The user data. 
 * $param len               The length of user data. 
 */ 
 typedef int timer_expiry(timer_id id, void *user_data, int len); 

 /** 
 * The type of the timer 
 */ 
 struct timer { 
        LIST_ENTRY(timer) entries;/**< list entry               */ 

        timer_id id;                /**< timer id                  */ 

        int interval;               /**< timer interval(second) */ 
        int elapse;                 /**< 0 -> interval             */ 

        timer_expiry *cb;          /**< call if expiry            */ 
        void *user_data;           /**< callback arg               */ 
        int len; 	                    /**< user_data length          */ 
 };

定時器的時間間隔以 interval 表示,而 elapse 則在 PerTickBookkeeping() 時遞增,直到 interval 表示定時器中止,此時呼叫回撥函式 cb 來執行相關的行為,而 user_data 和 len 為使用者可以傳遞給回撥函式的引數。

所有的定時器例項以連結串列來管理:


清單 4. 定時器連結串列
/** 
 * The timer list 
 */ 
 struct timer_list { 
        LIST_HEAD(listheader, timer) header;  /**< list header         */ 
        int num; 	                                   /**< timer entry number */ 
        int max_num;                               /**< max entry number    */ 

        void (*old_sigfunc)(int);                /**< save previous signal handler */ 
        void (*new_sigfunc)(int);                /**< our signal handler              */ 

        struct itimerval ovalue;                 /**< old timer value */ 
        struct itimerval value;                  /**< our internal timer value */ 
 };

這裡關於連結串列的實現使用了 BSD 風格關於連結串列的一組巨集,避免了再造輪子;該結構中,old_sigfunc 在 init_timer 初始定時器連結串列時候用來儲存系統對 SIGALRM 的處理函式,在定時器系統 destory 時用來恢復到之前的處理函式; ovalue 的用途與此類似。


清單 5. 定時器連結串列的建立和 Destroy
/** 
 * Create a timer list. 
 * 
 * @param count  The maximum number of timer entries to be supported initially. 
 * 
 * @return        0 means ok, the other means fail. 
 */ 
 int init_timer(int count) 
 { 
        int ret = 0; 

        if(count <=0 || count > MAX_TIMER_NUM) { 
               printf("the timer max number MUST less than %d.\n", MAX_TIMER_NUM); 
               return -1; 
        } 

        memset(&timer_list, 0, sizeof(struct timer_list)); 
        LIST_INIT(&timer_list.header); 
        timer_list.max_num = count; 

        /* Register our internal signal handler and store old signal handler */ 
        if ((timer_list.old_sigfunc = signal(SIGALRM, sig_func)) == SIG_ERR) { 
                return -1; 
        } 
        timer_list.new_sigfunc = sig_func; 

     /*Setting our interval timer for driver our mutil-timer and store old timer value*/ 
        timer_list.value.it_value.tv_sec = TIMER_START; 
        timer_list.value.it_value.tv_usec = 0; 
        timer_list.value.it_interval.tv_sec = TIMER_TICK; 
        timer_list.value.it_interval.tv_usec = 0; 
        ret = setitimer(ITIMER_REAL, &timer_list.value, &timer_list.ovalue); 

        return ret; 
 } 


 /** 
 * Destroy the timer list. 
 * 
 * @return          0 means ok, the other means fail. 
 */ 
 int destroy_timer(void) 
 { 
        struct timer *node = NULL; 

        if ((signal(SIGALRM, timer_list.old_sigfunc)) == SIG_ERR) { 
                return -1; 
        } 

        if((setitimer(ITIMER_REAL, &timer_list.ovalue, &timer_list.value)) < 0) { 
                return -1; 
        } 

        while (!LIST_EMPTY(&timer_list.header)) {     /* Delete. */ 
		 node = LIST_FIRST(&timer_list.header); 
                LIST_REMOVE(node, entries); 
                /* Free node */ 
		 printf("Remove id %d\n", node->id); 
                free(node->user_data); 
                free(node); 
        } 

        memset(&timer_list, 0, sizeof(struct timer_list)); 

        return 0; 
 }

新增定時器的動作非常的簡單,本質只是一個連結串列的插入而已:


清單 6. 向定時器連結串列中新增定時器
/** 
 * Add a timer to timer list. 
 * 
 * @param interval  The timer interval(second). 
 * @param cb  	    When cb!= NULL and timer expiry, call it. 
 * @param user_data Callback's param. 
 * @param len  	    The length of the user_data. 
 * 
 * @return          The timer ID, if == INVALID_TIMER_ID, add timer fail. 
 */ 
 timer_id  add_timer(int interval, timer_expiry *cb, void *user_data, int len) 
 { 
        struct timer *node = NULL; 

        if (cb == NULL || interval <= 0) { 
                return INVALID_TIMER_ID; 
        } 

	 if(timer_list.num < timer_list.max_num) { 
		 timer_list.num++; 
	 } else { 
		 return INVALID_TIMER_ID; 
	 } 

	 if((node = malloc(sizeof(struct timer))) == NULL) { 
		 return INVALID_TIMER_ID; 
	 } 
	 if(user_data != NULL || len != 0) { 
		 node->user_data = malloc(len); 
		 memcpy(node->user_data, user_data, len); 
		 node->len = len; 
	 } 

	 node->cb = cb; 
	 node->interval = interval; 
	 node->elapse = 0; 
	 node->id = timer_list.num; 

	 LIST_INSERT_HEAD(&timer_list.header, node, entries); 

    return node->id; 
 }

註冊的訊號處理函式則用來驅動定時器系統:


清單 7. 訊號處理函式驅動定時器
/* Tick Bookkeeping */ 
 static void sig_func(int signo) 
 { 
        struct timer *node = timer_list.header.lh_first; 
        for ( ; node != NULL; node = node->entries.le_next) { 
                node->elapse++; 
                if(node->elapse >= node->interval) { 
                        node->elapse = 0; 
                        node->cb(node->id, node->user_data, node->len); 
                } 
        } 
 }

它主要是在每次收到 SIGALRM 訊號時,執行定時器連結串列中的每個定時器 elapse 的自增操作,並與 interval 相比較,如果相等,代表註冊的定時器已經超時,這時則呼叫註冊的回撥函式。

上面的實現,有很多可以優化的地方:考慮另外一種思路,在定時器系統內部將維護的相對 interval 轉換成絕對時間,這樣,在每 PerTickBookkeeping 時,只需將當前時間與定時器的絕對時間相比較,就可以知道是否該定時器是否到期。這種方法,把遞增操作變為了比較操作。並且上面的實現方式,效率也不高,在執行 StartTimer,StopTimer,PerTickBookkeeping 時,演算法複雜度分別為 O(1),O(n),O(n),可以對上面的實現做一個簡單的改進,在 StartTimer 時,即在新增 Timer 例項時,對連結串列進行排序,這樣的改進,可以使得在執行 StartTimer,StopTimer,PerTickBookkeeping 時,演算法複雜度分別為 O(n),O(1),O(1) 。改進後的定時器系統如下圖 1:


圖 1. 基於排序連結串列的定時器
基於排序連結串列的定時器





基於 2.6 版本核心定時器的實現 (Posix 實時定時器 )

Linux 自 2.6 開始,已經開始支援 POSIX timer [ 2 ]所定義的定時器,它主要由下面的介面構成 :


清單 8. POSIX timer 介面
#include <signal.h> 
 #include <time.h> 

 int timer_create(clockid_t clockid, struct sigevent *evp, 
 timer_t *timerid); 
 int timer_settime(timer_t timerid, int flags, 
 const struct itimerspec *new_value, 
 struct itimerspec * old_value); 
 int timer_gettime(timer_t timerid, struct itimerspec *curr_value); 
 int timer_getoverrun(timer_t timerid); 
 int timer_delete(timer_t timerid);

這套介面是為了讓作業系統對實時有更好的支援,在連結時需要指定 -lrt 。

timer_create(2): 建立了一個定時器。

timer_settime(2): 啟動或者停止一個定時器。

timer_gettime(2): 返回到下一次到期的剩餘時間值和定時器定義的時間間隔。出現該介面的原因是,如果使用者定義了一個 1ms 的定時器,可能當時系統負荷很重,導致該定時器實際山 10ms 後才超時,這種情況下,overrun=9ms 。

timer_getoverrun(2): 返回上次定時器到期時超限值。

timer_delete(2): 停止並刪除一個定時器。

上面最重要的介面是 timer_create(2),其中,clockid 表明了要使用的時鐘型別,在 POSIX 中要求必須實現 CLOCK_REALTIME 型別的時鐘。 evp 引數指明瞭在定時到期後,呼叫者被通知的方式。該結構體定義如下 :


清單 9. POSIX timer 介面中的訊號和事件定義
union sigval { 
 int sival_int; 
 void *sival_ptr; 
 }; 

 struct sigevent { 
 int sigev_notify; /* Notification method */ 
 int sigev_signo; /* Timer expiration signal */ 
 union sigval sigev_value; /* Value accompanying signal or 
 passed to thread function */ 
 void (*sigev_notify_function) (union sigval); 
 /* Function used for thread 
 notifications (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 指明瞭通知的方式 :

SIGEV_NONE

當定時器到期時,不傳送非同步通知,但該定時器的執行進度可以使用 timer_gettime(2) 監測。

SIGEV_SIGNAL

當定時器到期時,傳送 sigev_signo 指定的訊號。

SIGEV_THREAD

當定時器到期時,以 sigev_notify_function 開始一個新的執行緒。該函式使用 sigev_value 作為其引數,當 sigev_notify_attributes 非空,則制定該執行緒的屬性。注意,由於 Linux 上執行緒的特殊性,這個功能實際上是由 glibc 和核心一起實現的。

SIGEV_THREAD_ID (Linux-specific)

僅推薦在實現執行緒庫時候使用。

如果 evp 為空的話,則該函式的行為等效於:sigev_notify = SIGEV_SIGNAL,sigev_signo = SIGVTALRM,sigev_value.sival_int = timer ID 。

由於 POSIX timer [ 2 ]介面支援在一個程序中同時擁有多個定時器例項,所以在上面的基於 setitimer() 和連結串列的 PerTickBookkeeping 動作就交由 Linux 核心來維護,這大大減輕了實現定時器的負擔。由於 POSIX timer [ 2 ]介面在定時器到期時,有更多的控制能力,因此,可以使用實時訊號避免訊號的丟失問題,並將 sigev_value.sival_int 值指定為 timer ID,這樣,就可以將多個定時器一起管理了。需要注意的是,POSIX timer [ 2 ]介面只在程序環境下才有意義 (fork(2) 和 exec(2) 也需要特殊對待 ),並不適合多執行緒環境。與此相類似的,Linux 提供了基於檔案描述符的相關定時器介面:


清單 10. Linux 提供的基於檔案描述符的定時器介面
#include <sys/timerfd.h> 

 int timerfd_create(int clockid, int flags); 
 int timerfd_settime(int fd, int flags, 
			 const struct itimerspec *new_value, 
			 struct itimerspec *old_value); 
 int timerfd_gettime(int fd, struct itimerspec *curr_value);

這樣,由於基於檔案描述符,使得該介面可以支援 select(2),poll(2) 等非同步介面,使得定時器的實現和使用更加的方便,更重要的是,支援 fork(2),exec(2) 這樣多程序的語義,因此,可以用在多執行緒環境之中,它們的使用比 POSIX timer [ 2 ]更加的靈活,其根本原因在於定時器的管理統一到了 unix/linux 基本哲學之一 ---- “一切皆檔案”之下。






最小堆實現的定時器

最小堆指的是滿足除了根節點以外的每個節點都不小於其父節點的堆。這樣,堆中的最小值就存放在根節點中,並且在以某個結點為根的子樹中,各節點的值都不小於該子樹根節點的值。一個最小堆的例子如下圖 2:


圖 2. 最小堆
最小堆

一個最小堆,一般支援以下幾種操作:

Insert(TimerHeap, Timer): 在堆中插入一個值,並保持最小堆性質,具體對應於定時器的實現,則是把定時器插入到定時器堆中。根據最小堆的插入演算法分析,可以知道該操作的時間複雜度為 O(lgn) 。

Minimum(TimerHeap): 獲取最小堆的中最小值;在定時器系統中,則是返回定時器堆中最先可能終止的定時器。由於是最小堆,只需返回堆的 root 即可。此時的演算法複雜度為 O(1) 。

ExtractMin(TimerHeap): 在定時器到期後,執行相關的動作,它的演算法複雜度為 O(1) 。

最小堆本質上是一種最小優先順序佇列 (min-priority queue) 。定時可以作為最小優先順序佇列的一個應用,該優先順序佇列把定時器的時間間隔值轉化為一個絕對時間來處理,ExtractMin 操則是在所有等待的定時器中,找出最先超時的定時器。在任何時候,一個新的定時器例項都可通過 Insert 操作加入到定時器佇列中去。

在 pjsip 專案的基礎庫 pjlib 中,有基於最小堆實現的定時器,它主要提供了以下的幾個介面:


清單 10. pjlib 提供的基於最小堆的定時器介面
/** 
 * Create a timer heap. 
 */ 
 PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, 
					   pj_size_t count, 
                                           pj_timer_heap_t **ht); 

 /** 
 * Destroy the timer heap. 
 */ 
 PJ_DECL(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ); 

 /** 
 * Initialize a timer entry. Application should call this function at least 
 * once before scheduling the entry to the timer heap, to properly initialize 
 * the timer entry. 
 */ 
 PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, 
                                              int id, 
                                              void *user_data, 
                                              pj_timer_heap_callback *cb ); 

 /** 
 * Schedule a timer entry which will expire AFTER the specified delay. 
 */ 
 PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, 
					     pj_timer_entry *entry, 
					     const pj_time_val *delay); 

 /** 
 * Cancel a previously registered timer. 
 */ 
 PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, 
				   pj_timer_entry *entry); 

 /** 
 * Poll the timer heap, check for expired timers and call the callback for 
 * each of the expired timers. 
 */ 
 PJ_DECL(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, 
                                      pj_time_val *next_delay);

pjlib 中的定時器在內部使用陣列的方式實現堆,這樣對於記憶體空間的使用將更加的緊湊;它的實現還可在定時器的數量超過預先設定的最大數量時會自己增加最大定時器數量。檔案 pjlib/src/pjlib-test/timer.c 是它的一個單元測試。與基於連結串列方式的實現相比較,明顯它的時間複雜度要低一些,這樣可以支援更多的定時器例項。






基於時間輪 (Timing-Wheel) 方式實現的定時器

時間輪 (Timing-Wheel) 演算法類似於一以恆定速度旋轉的左輪手槍,槍的撞針則撞擊槍膛,如果槍膛中有子彈,則會被擊發;與之相對應的是:對於 PerTickBookkeeping,其最本質的工作在於以 Tick 為單位增加時鐘,如果發現有任何定時器到期,則呼叫相應的 ExpiryProcessing 。設定一個迴圈為 N 個 Tick 單元,當前時間是在 S 個迴圈之後指向元素 i (i>=0 and i<= N - 1),則當前時間 (Current Time)Tc 可以表示為:Tc = S*N + i ;如果此時插入一個時間間隔 (Time Interval) 為 Ti 的定時器,設定它將會放入元素 n(Next) 中,則 n = (Tc + Ti)mod N = (S*N + i + Ti) mod N = (i + Ti) mod N 。如果我們的 N 足夠的大,顯然 StartTimer,StopTimer,PerTickBookkeeping 時,演算法複雜度分別為 O(1),O(1),O(1) 。在 [5] 中,給出了一個簡單定時器輪實現的定時。下圖 3 是一個簡單的時間輪定時器:


圖 3. 簡單時間輪
簡單時間輪

如果需要支援的定時器範圍非常的大,上面的實現方式則不能滿足這樣的需求。因為這樣將消耗非常可觀的記憶體,假設需要表示的定時器範圍為:0 – 2^3-1ticks,則簡單時間輪需要 2^32 個元素空間,這對於記憶體空間的使用將非常的龐大。也許可以降低定時器的精度,使得每個 Tick 表示的時間更長一些,但這樣的代價是定時器的精度將大打折扣。現在的問題是,度量定時器的粒度,只能使用唯一粒度嗎?想想日常生活中常遇到的水錶,如下圖 4:


圖 4. 水錶
水錶

在上面的水錶中,為了表示度量範圍,分成了不同的單位,比如 1000,100,10 等等,相似的,表示一個 32bits 的範圍,也不需要 2^32 個元素的陣列。實際上,Linux 的核心把定時器分為 5 組,每組的粒度分別表示為:1 jiffies,256 jiffies,256*64 jiffies,256*64*64 jiffies,256*64*64*64 jiffies,每組中桶的數量分別為:256,64,64,64,64,這樣,在 256+64+64+64+64 = 512 個桶中,表示的範圍為 2^32 。有了這樣的實現,驅動核心定時器的機制也可以通過水錶的例子來理解了,就像水錶,每個粒度上都有一個指標指向當前時間,時間以固定 tick 遞增,而當前時間指標則也依次遞增,如果發現當前指標的位置可以確定為一個註冊的定時器,就觸發其註冊的回撥函式。 Linux 核心定時器本質上是 Single-Shot Timer,如果想成為 Repeating Timer,可以在註冊的回撥函式中再次的註冊自己。核心定時器如下圖 5:


圖 5. Linux 時間輪
Linux時間輪





結論

由上面的分析,可以看到各種定時器實現演算法的複雜度:


表 1. 定時器實現演算法複雜度
實現方式 StartTimer StopTimer PerTickBookkeeping
基於連結串列 O(1) O(n) O(n)
基於排序連結串列 O(n) O(1) O(1)
基於最小堆 O(lgn) O(1) O(1)
基於時間輪 O(1) O(1) O(1)

如果需要能線上程環境中使用的定時器,對於基於連結串列的定時器,可能需要很小心的處理訊號的問題;而 POSIX timer [ 2 ]介面的定時器,只具有程序的語義,如果想在多執行緒環境下也 n 能使用,可以使用 Linux 提供的 timerfd_create(2) 介面。如果需要支援的定時器數量非常的大,可以考慮使用基於最小堆和時間輪的方式來實現。


相關推薦

Linux定時實現方式分析

級別: 初級 趙 軍 ([email protected]), 開發工程師, Pixelworks 2009 年 10 月 31 日 定時器屬於基本的基礎元件,不管是使用者空間的程式開發,還是核心空間的程式開發,很多時候都需要有定時器作為基礎元件的支援,

linux定時的使用--timer_create等函數集

rest eat 處理 stdio.h lag fin handle 之前 reat 程序1:采用新線程派駐的通知方式 程序2:通知方式為信號的處理方式 #include <stdio.h>#include <time.h>#include <

Linux定時的設定

1. alarm函式 [1] 引用標頭檔案:#include <unistd.h>; [2] 函式標準式:unsigned int alarm(unsigned int seconds); [3] 功能與作用:alarm()函式的主要功能是設定訊號傳送

linux定時setitimer的使用

1,下面為setitimer函式引數: int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue)); 第一個引數int switch為設定定時器型別:

linux定時的使用--timer_create等系列

程式1:採用新執行緒派駐的通知方式 #include <stdio.h> #include <signal.h> #include <time.h> #include <string.h> #include <stdli

linux定時timer_create()的使用

一、採用新執行緒派駐的方式 (注: 編譯時 需加上 -lrt)#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #inc

定時實現方式之TimerTask、Timer

一個 指定時間 rri public exit 退出程序 per 任務隊列 開始 在未來某個指定的時間點或者經過一段時間延遲後執行某個事件,這時候就需要用到定時器了。定時器的實現方式有很多種,今天總結最簡單的實現方式。java 1.3引入了定時器框架,用於在定時器上下文中控

LINUX使用一個定時實現設置任意數量定時

ftw rup () int stdlib.h val span 時鐘 sof 本例子參考 Don Libes的Title: Implementing Software Timers例子改寫 為什麽需要這個功能,因為大多數計算機軟件時鐘系統通常只能有一個時鐘觸發一次

MAC OS和Linux的crontab實現定時任務(執行python等指令碼)

前言 crontab命令常見於Unix和類Unix的作業系統中,用於設定週期性被執行的指令。該命令從標準輸入裝置讀指令,並將其存入到“crontab”檔案中,以供之後讀取和執行。 使用方法 1.crontab -e 編輯自定義自己的任務,儲存退出後自動新增到cron

linuxvlan的實現分析(上)

一. VLAN的核心概念     1. 劃分VLAN的核心目的只有一個:分割廣播域。        通過VLAN對廣播域進行合理分割之後,一是可以縮小ARP攻擊的範圍,從而提高網路的安全性;二是可以縮小廣播域的大小,從而提高網路的效能。        所以要注意的是,劃分V

Windows的高精度定時實現及精確時刻獲取

通訊、VOIP、視訊等領域的很多核心技術對時間精度的要求非常高,比如資料採集、時間同步、媒體流平滑控制、擁塞演算法等等,很多技術都是以毫秒為單位來進行計算和控制的。但是Windows設計之初並不是以實時系統為目標的,所以Windows系統的時間精度一直不高,實際最小單位是1

linux 定時 PHP定時實現每隔幾秒執行一次

PHP定時器實現每隔幾秒執行一次,下面寫個簡單例子來講解這個方法。 <?php  ignore_user_abort();//關閉瀏覽器仍然執行 set_time_limit(0);//讓程式一直執行下去 $interval=3;//每隔一定時間執行 do{   

linux讓irb實現代碼自己主動補全的功能

下載 article 一行代碼 技術 簡單 inux 我們 clu 童鞋 我不知道其它系統上irb是否有此功能,可是在ubuntu上ruby2.1.2自帶的irb默認是沒有代碼自己主動補全功能的,這多少讓人認為有所不便.事實上加上也非常easy,就是在irb裏載

LinuxFTPserver的實現(仿vsftpd)

stat 通信 ip地址 啟動 思想 ipp size_t ascii 上傳 繼上一篇博文實現Linux下的shell後,我們進一步利用網絡編程和系統編程的知識實現Linux下的FTPserver。我們以vsftpd為原型並實現了其大部分的功能。因為篇幅和時間的關系

windows和Linux定時啟動或關閉服務

ref sta article start 處理程序 window pin blog win http://blog.csdn.net/clare504/article/details/17410643 1、Windows下的定時啟動程序可以采用系統的計劃和任務,定時

linux使用rzsz實現文件的上傳和下載

輸入 ssh登錄 usr 終端 啟動 mode 裝包 ftw soft 新搞的雲服務器用SecureCRT不支持上傳和下載,沒有找到rz命令。記錄一下如何安裝rz/sz命令的方法。 一、工具說明 在SecureCRT這樣的ssh登錄軟件裏, 通過在L

linux采用binary方式安裝mysql步驟

glibc zxvf sql 創建用戶 啟動服務 star 安裝mysql mysql目錄 初始化 1、下載binary文件   在http://dev.mysql.com/downloads/mysql/官網上下載 mysql-5.6.36-linux-glibc2.5-

Linux Web服務網站故障分析常用的命令

linux awk 流量分析 系統連接狀態篇:1.查看TCP連接狀態netstat -nat |awk ‘{print $6}’|sort|uniq -c|sort -rnuniq 用於報告或忽略文件中的重復行uniq -c: 在每列旁邊顯示該行重復出現的次數;通過對文件中重復出現的字符進行統計接

Linux crontab定時設置(定期執行java程序)(轉)

在那 安裝 一次 tin 文件名 ani data 說明 ive Crontab 語法 Crontab語法一個crontab文件用五個段來定義:天,日期和時間,和一個要定期執行的命令代碼。 * * * * * command to be execut

ScheduledExecutorService--目前最理想的定時任務實現方式

run 工具 string java se oid -- 一次 void sta ScheduledExecutorService它是從Java SE5的java.util.concurrent裏,做為並發工具類被引進的,這是目前最理想的定時任務實現方式。 相比於上兩個方法