linux 時間&定時器 介紹

1.時間表示

  在程序當中,我們經常要輸出系統當前的時間,比如我們使用date命令的輸出結果.這個時候我們可以使用下面兩個函數:

  #include

  time_t time(time_t *tloc);

  char *ctime(const time_t *clock);

  time函數返回從1970年1月1日0點以來的秒數.存儲在time_t結構之中.不過這個函數的返回值對於我們來說沒有什麼實際意義.這個時候我們使用第二個函數將秒數轉化爲字符串. 這個函數的返回類型是固定的:一個可能值爲.Thu Dec7 14:58:59 2000 這個字符串的長度是固定的爲26.

2.時間的測量

  有時候我們要計算程序執行的時間.比如我們要對算法進行時間分析.這個時候可以使用下面這個函數. #include

  int gettimeofday(struct timeval *tv,struct timezone *tz);

  strut timeval {

  long tv_sec; /* 秒數 */

  long tv_usec; /* 微秒數 */

  };

  gettimeofday將時間保存在結構tv之中.tz一般我們使用NULL來代替.

3.計時器的使用


3.1、alarm
-------------------------------------------
    如果不要求很精確的話,用alarm()和signal()就夠了
    unsigned int alarm(unsigned int seconds)
    函數說明: alarm()用來設置信號SIGALRM在經過參數seconds指定的秒數後傳送給目前的進程。如果參數seconds爲0,則之前設置的鬧鐘會被取消,並將剩下的時間返回。
    返回值: 返回之前鬧鐘的剩餘秒數,如果之前未設鬧鐘則返回0。
    alarm()執行後,進程將繼續執行,在後期(alarm以後)的執行過程中將會在seconds秒後收到信號SIGALRM並執行其處理函數。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigalrm_fn(int sig)
{
    printf("alarm!/n");
    alarm(2);
    return;
}
int main(void)
{
    signal(SIGALRM, sigalrm_fn);
    alarm(1);
    while(1) pause();
}


3.2、setitimer()
-------------------------------------------
    int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
    setitimer()比alarm功能強大,支持3種類型的定時器:
    ITIMER_REAL :     以系統真實的時間來計算,它送出SIGALRM信號。
    ITIMER_VIRTUAL : -以該進程在用戶態下花費的時間來計算,它送出SIGVTALRM信號。
    ITIMER_PROF :     以該進程在用戶態下和內核態下所費的時間來計算,它送出SIGPROF信號。
    setitimer()第一個參數which指定定時器類型(上面三種之一);第二個參數是結構itimerval的一個實例;第三個參數可不做處理。
    setitimer()調用成功返回0,否則返回-1。

    下面是關於setitimer調用的一個簡單示範,在該例子中,每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM信號:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
int sec;
void sigroutine(int signo){
    switch (signo){
        case SIGALRM:
            printf("Catch a signal -- SIGALRM /n");
            signal(SIGALRM, sigroutine);
            break;
        case SIGVTALRM:
            printf("Catch a signal -- SIGVTALRM /n");
            signal(SIGVTALRM, sigroutine);
            break;
    }
    return;
}
int main()
{
    struct itimerval value, ovalue, value2;          //(1)
    sec = 5;
    printf("process id is %d/n", getpid());
    signal(SIGALRM, sigroutine);
    signal(SIGVTALRM, sigroutine);
    value.it_value.tv_sec = 1;
    value.it_value.tv_usec = 0;
    value.it_interval.tv_sec = 1;
    value.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &value, &ovalue);     //(2)
    value2.it_value.tv_sec = 0;
    value2.it_value.tv_usec = 500000;
    value2.it_interval.tv_sec = 0;
    value2.it_interval.tv_usec = 500000;
    setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
    for(;;)
        ;
}
(1) struct itimerval
struct itimerval {
    struct timeval it_interval; /* timer interval */
    struct timeval it_value;    /* current value */
};
itimerval:      i   --> interval
                val --> value        
    itimerval結構中的it_value是減少的時間,當這個值爲0的時候就發出相應的信號了. 然後再將it_value設置爲it_interval值.
(2) setitimer()
    setitimer()爲其所在進程設置一個定時器,如果itimerval.it_interval不爲0(it_interval的兩個域都不爲0),則該定時器將持續有效(每隔一段時間就會發送一個信號)
    注意:Linux信號機制基本上是從Unix系統中繼承過來的。早期Unix系統中的信號機制比較簡單和原始,後來在實踐中暴露出一些問題,因此,把那些建立在早期機制上的信號叫做"不可靠信號",信號值小於SIGRTMIN(SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。這就是"不可靠信號"的來源。它的主要問題是:進程每次處理信號後,就將對信號的響應設置爲默認動作。在某些情況下,將導致對信號的錯誤處理;因此,用戶如果不希望這樣的操作,那麼就要在信號處理函數結尾再一次調用signal(),重新安裝該信號。

***********************************

 

Linux下如何實現秒以下精確定時與休眠

 Linux中提供的休眠函數是sleep和alarm,但是他們僅僅提供以秒爲單位的休眠,這中休眠有些進程顯然太長了,那麼怎樣才能使進程以更小的時間分辨率休眠呢?
 我知道的方法有2種,下面就做分別介紹。
 第一種方法是使用定時器,Linux提供的定時器函數是:
 int  setitimer(int which, const struct itimerval *value, struct
         itimerval *ovalue);
 which指定那種定時器。Linux提供3種定時器:
 TIMER_REAL: 準確定時器,超時會發出SIGALRM信號;
 TIMER_VIRTUAL: 虛擬定時器,只記進程時間,所以會根據進程執行時間而變化,不能實現準確定時,超時發出SIGVTALRM信號;
 TIMER_PROF: 梗概計時器,它會根據進程時間和系統時間而變化,不能實現準確定時,超時發出SIGPROF信號;
 在進程中應該捕捉所設定時器會發出的信號,因爲進程收到定時器超時發出的信號後,默認動作是終止。
 value是設置定時器時間,相關結構如下:
 struct itimerval {
      struct timeval it_interval;
      struct timeval it_value;
 };
 struct timeval {
      long tv_sec;       
      long tv_usec;
 };
  it_interval指定間隔時間,it_value指定初始定時時間。如果只指定it_value,就是實現一次定時;如果同時指定 it_interval,則超時後,系統會重新初始化it_value爲it_interval,實現重複定時;兩者都清零,則會清除定時器。
 tv_sec提供秒級精度,tv_usec提供微秒級精度,以值大的爲先,注意1s = 1000000ms。
 ovalue用來保存先前的值,常設爲NULL。
 如果是以setitimer提供的定時器來休眠,只需阻塞等待定時器信號就可以了。
 第二種方法是使用select來提供精確定時和休眠:
 int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
       struct timeval *timeout);
 n指監視的文件描述符範圍,通常設爲所要select的fd+1,readfds,writefds和exceptfds分別是讀,寫和異常文件描述符集,timeout爲超時時間。
 可能用到的關於文件描述符集操作的宏有:
 FD_CLR(int fd, fd_set *set);    清除fd
 FD_ISSET(int fd, fd_set *set);  測試fd是否設置
 FD_SET(int fd, fd_set *set);     設置fd
 FD_ZERO(fd_set *set);             清空描述符集
 我們此時用不到這些宏,因爲我們並不關心文件描述符的狀態,我們關心的是select超時。所以我們需要把readfds,writefds和exceptfds都設爲NULL,只指定timeout時間就行了。至於n我們可以不關心,所以你可以把它設爲任何非負值。實現代碼如下:

 int msSleep(long ms) {

    struct timeval tv;

    tv.tv_sec = 0;

    tv.tv_usec = ms;

    return select(0, NULL, NULL, NULL, &tv);

 }


 呵呵,怎麼樣,是不是很簡單?
 結語:
  setitimer和select都能實現進程的精確休眠,本文分別對他們進行了簡單介紹,並給出了一個簡單的給予select的實現。我不推薦使用 setitimer,因爲一者Linux系統提供的timer有限(每個進程至多能設3個不同類型的timer),再者ssetitimer實現起來沒有 select簡單。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/zhuky/archive/2009/12/17/5018511.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章