linux系統程式設計之訊號(八):三種時間結構及定時器setitimer()詳解

mickole發表於2013-07-15

一,三種時間結構

time_t://seconds

 

struct timeval {

long tv_sec; /* seconds */

long tv_usec; /* microseconds */

};

 

struct timespec {

time_t tv_sec; /* seconds */

long tv_nsec; /* nanoseconds */

};

二,setitimer()

現在的系統中很多程式不再使用alarm呼叫,而是使用setitimer呼叫來設定定時器,用getitimer來得到定時器的狀態,

這兩個呼叫的宣告格式如下:

#include <sys/time.h>

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

引數:

  • 第一個引數which指定定時器型別
  • 第二個引數是結構itimerval的一個例項,結構itimerval形式
  • 第三個引數可不做處理。

返回值:成功返回0失敗返回-1

該系統呼叫給程式提供了三個定時器,它們各自有其獨有的計時域,當其中任何一個到達,就傳送一個相應的訊號給程式,並使得計時器重新開始。三個計時器由引數which指定,如下所示:

TIMER_REAL:按實際時間計時,計時到達將給程式傳送SIGALRM訊號。

ITIMER_VIRTUAL:僅當程式執行時才進行計時。計時到達將傳送SIGVTALRM訊號給程式。

ITIMER_PROF:當程式執行時和系統為該程式執行動作時都計時。與ITIMER_VIR-TUAL是一對,該定時器經常用來統計程式在使用者態和核心態花費的時間。計時到達將傳送SIGPROF訊號給程式。

定時器中的引數value用來指明定時器的時間,其結構如下:

struct itimerval {

        struct timeval it_interval; /* 第一次之後每隔多長時間 */

        struct timeval it_value; /* 第一次呼叫要多長時間 */

};

該結構中timeval結構定義如下:

struct timeval {

        long tv_sec; /* 秒 */

        long tv_usec; /* 微秒,1秒 = 1000000 微秒*/

};

在setitimer 呼叫中,引數ovalue如果不為空,則其中保留的是上次呼叫設定的值。定時器將it_value遞減到0時,產生一個訊號,並將it_value的值設定為it_interval的值,然後重新開始計時,如此往復。當it_value設定為0時,計時器停止,或者當它計時到期,而it_interval 為0時停止。呼叫成功時,返回0;錯誤時,返回-1,並設定相應的錯誤程式碼errno:

EFAULT:引數value或ovalue是無效的指標。

EINVAL:引數which不是ITIMER_REAL、ITIMER_VIRT或ITIMER_PROF中的一個。

示例一:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

void handler(int sig)
{
    printf("recv a sig=%d\n", sig);
}

int main(int argc, char *argv[])
{
    if (signal(SIGALRM, handler) == SIG_ERR)
        ERR_EXIT("signal error");

    struct timeval tv_interval = {1, 0};
    struct timeval tv_value = {5, 0};
    struct itimerval it;
    it.it_interval = tv_interval;
    it.it_value = tv_value;
    setitimer(ITIMER_REAL, &it, NULL);

    for (;;)
        pause();
    return 0;
}

結果:

QQ截圖20130715202529

可以看到第一次傳送訊號是在5s以後,之後每隔一秒傳送一次訊號

示例二:獲得產生時鐘訊號的剩餘時間

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    struct timeval tv_interval = {1, 0};
    struct timeval tv_value = {1, 0};
    struct itimerval it;
    it.it_interval = tv_interval;
    it.it_value = tv_value;
    setitimer(ITIMER_REAL, &it, NULL);

    int i;
    for (i=0; i<10000; i++);

//第一種方式獲得剩餘時間
    struct itimerval oit;
    setitimer(ITIMER_REAL, &it, &oit);//利用oit獲得剩餘時間產生時鐘訊號
    printf("%d %d %d %d\n", (int)oit.it_interval.tv_sec, (int)oit.it_interval.tv_usec, (int)oit.it_value.tv_sec, (int)oit.it_value.tv_usec);
//第二種方式獲得剩餘時間
    //getitimer(ITIMER_REAL, &it);
    //printf("%d %d %d %d\n", (int)it.it_interval.tv_sec, (int)it.it_interval.tv_usec, (int)it.it_value.tv_sec, (int)it.it_value.tv_usec);

    return 0;
}

結果:

用第一種方式:

QQ截圖20130715204042

用第二種方式:利用getitimer在不重新設定時鐘的情況下獲取剩餘時間

QQ截圖20130715204130

剩餘時間是指:距離下一次呼叫定時器產生訊號所需時間,這裡由於for迴圈不到一秒就執行完,定時器還來不及產生時鐘訊號,所以有剩餘時間

示例三:每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM訊號

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>

void sigroutine(int signo)
{
        switch (signo) {
        case SIGALRM:
        printf("Catch a signal -- SIGALRM\n ");
        break;
        case SIGVTALRM:
        printf("Catch a signal -- SIGVTALRM\n ");
        break;
        }
        return;
}

int main()
{

       struct itimerval value,value2;
        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,NULL);


        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,NULL);

        for (;;) ;

}

結果:

QQ截圖20130715205534

可知確實是沒兩次SIGVTALRM一次SIGALRM

相關文章