歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> linux系統中的定時器

linux系統中的定時器

日期:2017/3/3 12:42:42   编辑:Linux技術

概論

不管是用戶空間的程序開發,還是內核空間的程序開發,很多時候都需要有定時器的支持,定時器屬於程序開發中的基本組件。定時器一般按照使用場景分為兩種類型:1.Single-Shot Timer

2.Repeating Timer

其中第一種定時器,從注冊到終止只執行一次,而第二種定時器,在每次執行完以後,自動重新開始。本質上,可以認為 Repeating Timer 是在 Single-Shot Timer 終止之後,再次注冊到定時器系統裡的 Single-Shot Timer,因此,在支持 Single-Shot Timer 的基礎上支持 Repeating Timer 並不算特別的復雜。

Linux定時器

2.4內核版本linux 結構體:

[code]struct itimerval { 
    struct timeval it_interval; /* next value */ 
    struct timeval it_value;     /* current value */ 
}; 

struct timeval { 
    long tv_sec;                /* seconds */ 
    long tv_usec;               /* microseconds */ 
 };
user api函數:

[code]#include <sys/time.h> 
int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
傳入參數:

which:

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

ITIMER_VIRTUAL 僅進程在用戶空間執行時遞減,在到期之後發送 SIGVTALRM 信號

ITIMER_PROF 進程在用戶空間執行以及內核為該進程服務時 ( 典型如完成一個系統調用 ) 都會遞減,與 ITIMER_VIRTUAL 共用時可度量該應用在內核空間和用戶空間的時間消耗情況,在到期之後發送 SIGPROF 信號

new_value:

新設置的定時器時間參數。

old_value:

返回的舊的定時器時間。

setitimer 能夠在 timer 到期之後,自動再次啟動自己,因此,用它來解決 Single-Shot Timer 和 Repeating Timer 的問題顯得很簡單。

2.6 內核版本linux 除了上面提到的接口,2.6版本內核以後,都增加了POSIX timer接口API。

[code]#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: 創建了一個定時器。

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

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

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

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

上面最重要的接口是timer_create,其中,clockid 表明了要使用的時鐘類型,在 POSIX 中要求必須實現 CLOCK_REALTIME 類型的時鐘。 evp 參數指明了在定時到期後,調用者被通知的方式。該結構體定義如下 :

[code]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 

 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監測。

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 接口支持在一個進程中同時擁有多個定時器實例。但是需要注意的是,POSIX timer 接口只在進程環境下才有意義 ,並不適合多線程環境。因此,Linux 提供了基於文件描述符的相關定時器接口:

[code]#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,poll 等異步接口,使得定時器的實現和使用更加的方便,它們的使用比 POSIX timer 更加的靈活。

自定義定時器

為了同時支持2.4以及2.6版本以上的內核環境,我們可以考慮自己實現定時器來進行統一管理。 在接下來將實現一個簡單的定時器模型,將基於上面提到的setitimer接口來實現,此接口在linux系統中都存在。

為什麼要實現自定義定時器,首先,2.4內核中沒有posix timer接口,而setitimer接口只能允許一個進程中存在一個定時器,那麼當我們需要在進程中使用多個定時器時就束手無措,另外也是為了程序上的兼容,所以我們接下來將介紹如何實現自己的定時器。

[code]#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include "list.h"
/**
* @file name: timer.c
* @author:xhc
* @date:2016.5.17
*
*/
#define MAX_TIMER_NUM 50

typedef void (*callback)(int id, void *data, int len);

typedef void (*SIG_FUNC)(int signo);

typedef struct timer{
    struct list_head node;
    int interval; /*timer interval(second)*/
    int elapse;   /*timer count*/

    callback cb; /*call back function*/
    void *user;  /*user data*/
    int len;
    int id; /*timerid*/
}timer_node_t;

struct timer_list{
    struct list_head head;
    int num;
    int size;
    void (*sighandler_old)(int);
    void (*sighandler)(int);
    struct itimerval ovalue;
    struct itimerval value;
};

/**
* Local data
*/
static struct timer_list *timer_list = NULL;

static void sig_func(int signo) 
{ 
    struct list_head *node;
    struct list_head *tmp;
    timer_node_t *timer;    
    if(list_empty(&timer_list->head) == true){
        return;
    }
    list_for_each_safe(node, tmp, &timer_list->head) {
        timer = list_entry(node, struct timer, node);
        timer->elapse++; 
                if(timer->elapse >= timer->interval) { 
                        timer->elapse = 0; 
                        timer->cb(timer->id, timer->user, timer->len); 
                }
    }
}

/**
*Create timer list
*@param
*/

struct timer_list *create_timer_list(int count) 
{ 
        int ret = 0; 
    struct timer_list *ptr = NULL;

        if(count <=0 || count > MAX_TIMER_NUM) { 
               printf("the timer max number too big, MAX num is %d.\n", MAX_TIMER_NUM); 
               return NULL; 
        } 

    ptr = (struct timer_list *)malloc(sizeof(struct timer_list));
        memset(ptr, 0, sizeof(struct timer_list)); 
        INIT_LIST_HEAD(&ptr->head);
        ptr->size = count;

        /* Register our internal signal handler and store old signal handler */ 
        if ((ptr->sighandler_old = signal(SIGALRM, sig_func)) == SIG_ERR) { 
                goto err_out; 
        } 
        ptr->sighandler = sig_func; 

        ptr->value.it_value.tv_sec = 1;  /*for firt timeout*/
        ptr->value.it_value.tv_usec = 0; 
        ptr->value.it_interval.tv_sec = 1; /*for tick reload*/
        ptr->value.it_interval.tv_usec = 0; 
        ret = setitimer(ITIMER_REAL, &ptr->value, &ptr->ovalue); 
    if (ret < 0)
        goto err_out;
        return ptr; 
err_out:
    printf("create_timer_list error\n");
    free(ptr);
    return NULL;
 } 

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

        signal(SIGALRM, list->sighandler_old);
        setitimer(ITIMER_REAL, &list->ovalue, &list->value);

    if(list_empty(&list->head) == false){
        struct list_head *node;
        struct list_head *tmp;
        timer_node_t *timer;    
        list_for_each_safe(node, tmp, &list->head) {
            timer = list_entry(node, struct timer, node);
            list_del(node); 
            free(timer->user);
            free(timer);
            timer = NULL;
        }
    }
        free(list);
        return 0; 
 }

/** 
 * 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          if == -1, add timer fail. 
 */ 
 int  add_timer(int interval, callback cb, void *user_data, int len) 
 { 
    struct timer *node = NULL; 

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

    if(timer_list->num < timer_list->size) { 
        timer_list->num++; 
    } else { 
        return -1; 
    } 

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

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

    list_add_tail(&node->node, &timer_list->head); 

    return node->id; 
}

 int  del_timer(int timer_id) 
 { 
    struct list_head *node;
    struct list_head *tmp;
    timer_node_t *timer;    

    if(timer_list == NULL) {
        return -1; 
    } 

    if(list_empty(&timer_list->head) == true){
        return -1;
    }

    list_for_each_safe(node, tmp, &timer_list->head) {
        timer = list_entry(node, struct timer, node);
        if (timer->id == timer_id){
            list_del(node); 
            free(timer->user);
            free(timer);
            return 0;
        }
        timer = NULL;
    }
    return -1;
}

/****************demo*******************/

void timer_test_callback(int id, void *data, int len)
{
    int *user = (int *)data;
    printf("timer id is :%d\n",id);
    printf("data is : %d\n", *user);
    printf("print this for every 5 sec!!!\n");
    user[0]++;
}

int main()
{
    int timer_id = -1;
    int count = 0;
    timer_list = create_timer_list(10);
    timer_id = add_timer(5, timer_test_callback, &count, 4);
    while(count++ < 20)
        sleep(1);
    del_timer(timer_id);
    destroy_timer(timer_list);
}
以上就是我們自己實現的timer管理器,還是有待優化的地方,比如采用排序鏈表,來優化查找復雜度,感興趣的讀者可以自己嘗試修改。

Copyright © Linux教程網 All Rights Reserved