2012-10-06 164 views
5

可能重複:
Loops/timers in C計時器在linux在C

我一直在閱讀有關計時器的最後3天,我無法找到任何有用的,我我試圖在實際的例子中理解它,有人可以幫我弄清楚如何爲下面的程序設置警報。

如何設置一個計時器,以便它將發送2個參數,一個是數組名稱,第二個是要刪除的數字,我知道下面的代碼在任何情況下都不安全,我只是嘗試瞭解如何使用帶有args的報警來調用函數。

請注意,環境是Linux,也很欣賞任何與工作C例子的鏈接。

#include<stdio.h> 
int delete_from_array(int arg) ; 


    int main() 
    { 

    int a[10000], i, y ; 
    //how to set timer here for to delete any number in array after half a second 
    for (y=0; y < 100; y++) { 


     for (i=0; i<sizeof(a)/sizeof(int); i++) 
      a[i] = i; 
    sleep(1); 
    printf("wake\n"); 
    } 

    } 

    int delete_from_array(int arg) 
    { 
    int i, a[1000], number_to_delete=0; 

    //number_to_delete = arg->number; 

    for (i=0; i<sizeof(a); i++) 
     if (a[i] == number_to_delete) 
      a[i] = 0; 
    printf("deleted\n"); 

    } 

我想要做的是,我有一個已經有編號值1秒後過期的哈希,所以我該值插入散後,我需要創建一個定時器,使得它假設我在1秒之後刪除該值,並且如果在該間隔(1秒)之前從服務器獲得響應,則我從哈希中刪除該值並刪除該計時器,就像tcp中的重傳一樣

+0

這個'i

+2

你可能想檢查[this](http://stackoverflow.com/questions/5540245/loops-timers-in-c)question – tomahh

+0

我的錯誤,我的意思是sizeof(a)/ sizeof(int),我會修改代碼。 –

回答

19

你想使用信號還是線程?

首先,設置信號處理程序或準備合適的線程函數;詳情請參閱man 7 sigevent

接下來,創建一個合適的計時器,使用timer_create()。詳情請參閱man 2 timer_create

根據您在計時器啓動時所做的操作,您可能希望將計時器設置爲單次計時,或者在之後的短時間間隔內重複計時。你使用timer_settime()來同時設定和撤防計時器;詳情請參閱man 2 timer_settime

在實際應用中,您通常需要複用計時器。即使一個進程可以創建多個定時器,它們也是有限的資源。特別是超時定時器 - 無論是設置一個標誌和/或發送一個信號給特定的線程都很簡單 - 應該使用一個定時器,該定時器在下一個超時時觸發,設置相關的超時標誌,並可選地發送一個信號(與一個空體處理程序)到所需的線程,以確保它被打斷。 (對於單線程進程,原始信號傳遞會中斷阻塞的I/O調用。)考慮服務器,響應某些請求:請求本身在處理請求時可能有一分鐘左右的超時可能需要連接超時,I/O超時等。

現在,最初的問題很有趣,因爲定時器在有效使用時功能強大。但是,示例程序基本上是無稽之談。你爲什麼不創建一個程序來設置一個或多個定時器,每個定時器例如輸出一些東西到標準輸出?記得使用來自unistd.hwrite()等,因爲它們是async-signal safe,而等等,而不是來自stdio.h。 (如果你的信號處理程序使用非異步信號安全函數,結果是不確定的,它通常是有效的,但它並不能保證;它可能和工作一樣崩潰,測試不會告訴,因爲它是undefined 。)


編輯補充:這裏是一個簡單的多路複用超時的例子。 (儘可能根據法律規定,我將以下所示代碼片段的所有版權及相關權利和鄰接權歸於全球公共領域;請參閱CC0 Public Domain Dedication。換句話說,請隨時以任何方式使用以下代碼你想,就不要怪我與它的任何問題。)

我用舊式GCC原子內置插件,所以它應該是線程安全的。除了一些補充之外,它也適用於多線程代碼。 (您不能使用例如互斥,因爲pthread_mutex_lock()不是異步信號安全。以原子操作超時狀態應該工作,雖然可能有一些比賽留給如果禁用只是當它觸發超時。)

#define _POSIX_C_SOURCE 200809L 
#include <unistd.h> 
#include <signal.h> 
#include <time.h> 
#include <errno.h> 

#define TIMEOUTS  16 
#define TIMEOUT_SIGNAL (SIGRTMIN+0) 

#define TIMEOUT_USED 1 
#define TIMEOUT_ARMED 2 
#define TIMEOUT_PASSED 4 

static timer_t    timeout_timer; 
static volatile sig_atomic_t timeout_state[TIMEOUTS] = { 0 }; 
static struct timespec  timeout_time[TIMEOUTS]; 


/* Return the number of seconds between before and after, (after - before). 
* This must be async-signal safe, so it cannot use difftime(). 
*/ 
static inline double timespec_diff(const struct timespec after, const struct timespec before) 
{ 
    return (double)(after.tv_sec - before.tv_sec) 
     + (double)(after.tv_nsec - before.tv_nsec)/1000000000.0; 
} 

/* Add positive seconds to a timespec, nothing if seconds is negative. 
* This must be async-signal safe. 
*/ 
static inline void timespec_add(struct timespec *const to, const double seconds) 
{ 
    if (to && seconds > 0.0) { 
     long s = (long)seconds; 
     long ns = (long)(0.5 + 1000000000.0 * (seconds - (double)s)); 

     /* Adjust for rounding errors. */ 
     if (ns < 0L) 
      ns = 0L; 
     else 
     if (ns > 999999999L) 
      ns = 999999999L; 

     to->tv_sec += (time_t)s; 
     to->tv_nsec += ns; 

     if (to->tv_nsec >= 1000000000L) { 
      to->tv_nsec -= 1000000000L; 
      to->tv_sec++; 
     } 
    } 
} 

/* Set the timespec to the specified number of seconds, or zero if negative seconds. 
*/ 
static inline void timespec_set(struct timespec *const to, const double seconds) 
{ 
    if (to) { 
     if (seconds > 0.0) { 
      const long s = (long)seconds; 
      long  ns = (long)(0.5 + 1000000000.0 * (seconds - (double)s)); 

      if (ns < 0L) 
       ns = 0L; 
      else 
      if (ns > 999999999L) 
       ns = 999999999L; 

      to->tv_sec = (time_t)s; 
      to->tv_nsec = ns; 

     } else { 
      to->tv_sec = (time_t)0; 
      to->tv_nsec = 0L; 
     } 
    } 
} 


/* Return nonzero if the timeout has occurred. 
*/ 
static inline int timeout_passed(const int timeout) 
{ 
    if (timeout >= 0 && timeout < TIMEOUTS) { 
     const int state = __sync_or_and_fetch(&timeout_state[timeout], 0); 

     /* Refers to an unused timeout? */ 
     if (!(state & TIMEOUT_USED)) 
      return -1; 

     /* Not armed? */ 
     if (!(state & TIMEOUT_ARMED)) 
      return -1; 

     /* Return 1 if timeout passed, 0 otherwise. */ 
     return (state & TIMEOUT_PASSED) ? 1 : 0; 

    } else { 
     /* Invalid timeout number. */ 
     return -1; 
    } 
} 

/* Release the timeout. 
* Returns 0 if the timeout had not fired yet, 1 if it had. 
*/ 
static inline int timeout_unset(const int timeout) 
{ 
    if (timeout >= 0 && timeout < TIMEOUTS) { 
     /* Obtain the current timeout state to 'state', 
     * then clear all but the TIMEOUT_PASSED flag 
     * for the specified timeout. 
     * Thanks to Bylos for catching this bug. */ 
     const int state = _sync_fetch_and_and(&timeout_state[timeout], TIMEOUT_PASSED); 

     /* Invalid timeout? */ 
     if (!(state & TIMEOUT_USED)) 
      return -1; 

     /* Not armed? */ 
     if (!(state & TIMEOUT_ARMED)) 
      return -1; 

     /* Return 1 if passed, 0 otherwise. */ 
     return (state & TIMEOUT_PASSED) ? 1 : 0; 

    } else { 
     /* Invalid timeout number. */ 
     return -1; 
    } 
} 


int timeout_set(const double seconds) 
{ 
    struct timespec now, then; 
    struct itimerspec when; 
    double   next; 
    int    timeout, i; 

    /* Timeout must be in the future. */ 
    if (seconds <= 0.0) 
     return -1; 

    /* Get current time, */ 
    if (clock_gettime(CLOCK_REALTIME, &now)) 
     return -1; 

    /* and calculate when the timeout should fire. */ 
    then = now; 
    timespec_add(&then, seconds); 

    /* Find an unused timeout. */ 
    for (timeout = 0; timeout < TIMEOUTS; timeout++) 
     if (!(__sync_fetch_and_or(&timeout_state[timeout], TIMEOUT_USED) & TIMEOUT_USED)) 
      break; 

    /* No unused timeouts? */ 
    if (timeout >= TIMEOUTS) 
     return -1; 

    /* Clear all but TIMEOUT_USED from the state, */ 
    __sync_and_and_fetch(&timeout_state[timeout], TIMEOUT_USED); 

    /* update the timeout details, */ 
    timeout_time[timeout] = then; 

    /* and mark the timeout armable. */ 
    __sync_or_and_fetch(&timeout_state[timeout], TIMEOUT_ARMED); 

    /* How long till the next timeout? */ 
    next = seconds; 
    for (i = 0; i < TIMEOUTS; i++) 
     if ((__sync_fetch_and_or(&timeout_state[i], 0) & (TIMEOUT_USED | TIMEOUT_ARMED | TIMEOUT_PASSED)) == (TIMEOUT_USED | TIMEOUT_ARMED)) { 
      const double secs = timespec_diff(timeout_time[i], now); 
      if (secs >= 0.0 && secs < next) 
       next = secs; 
     } 

    /* Calculate duration when to fire the timeout next, */ 
    timespec_set(&when.it_value, next); 
    when.it_interval.tv_sec = 0; 
    when.it_interval.tv_nsec = 0L; 

    /* and arm the timer. */ 
    if (timer_settime(timeout_timer, 0, &when, NULL)) { 
     /* Failed. */ 
     __sync_and_and_fetch(&timeout_state[timeout], 0); 
     return -1; 
    } 

    /* Return the timeout number. */ 
    return timeout; 
} 


static void timeout_signal_handler(int signum __attribute__((unused)), siginfo_t *info, void *context __attribute__((unused))) 
{ 
    struct timespec now; 
    struct itimerspec when; 
    int    saved_errno, i; 
    double   next; 

    /* Not a timer signal? */ 
    if (!info || info->si_code != SI_TIMER) 
     return; 

    /* Save errno; some of the functions used may modify errno. */ 
    saved_errno = errno; 

    if (clock_gettime(CLOCK_REALTIME, &now)) { 
     errno = saved_errno; 
     return; 
    } 

    /* Assume no next timeout. */ 
    next = -1.0; 

    /* Check all timeouts that are used and armed, but not passed yet. */ 
    for (i = 0; i < TIMEOUTS; i++) 
     if ((__sync_or_and_fetch(&timeout_state[i], 0) & (TIMEOUT_USED | TIMEOUT_ARMED | TIMEOUT_PASSED)) == (TIMEOUT_USED | TIMEOUT_ARMED)) { 
      const double seconds = timespec_diff(timeout_time[i], now); 
      if (seconds <= 0.0) { 
       /* timeout [i] fires! */ 
       __sync_or_and_fetch(&timeout_state[i], TIMEOUT_PASSED); 

      } else 
      if (next <= 0.0 || seconds < next) { 
       /* This is the soonest timeout in the future. */ 
       next = seconds; 
      } 
     } 

    /* Note: timespec_set() will set the time to zero if next <= 0.0, 
    *  which in turn will disarm the timer. 
    * The timer is one-shot; it_interval == 0. 
    */ 
    timespec_set(&when.it_value, next); 
    when.it_interval.tv_sec = 0; 
    when.it_interval.tv_nsec = 0L; 
    timer_settime(timeout_timer, 0, &when, NULL); 

    /* Restore errno. */ 
    errno = saved_errno; 
} 


int timeout_init(void) 
{ 
    struct sigaction act; 
    struct sigevent evt; 
    struct itimerspec arm; 

    /* Install timeout_signal_handler. */ 
    sigemptyset(&act.sa_mask); 
    act.sa_sigaction = timeout_signal_handler; 
    act.sa_flags = SA_SIGINFO; 
    if (sigaction(TIMEOUT_SIGNAL, &act, NULL)) 
     return errno; 

    /* Create a timer that will signal to timeout_signal_handler. */ 
    evt.sigev_notify = SIGEV_SIGNAL; 
    evt.sigev_signo = TIMEOUT_SIGNAL; 
    evt.sigev_value.sival_ptr = NULL; 
    if (timer_create(CLOCK_REALTIME, &evt, &timeout_timer)) 
     return errno; 

    /* Disarm the timeout timer (for now). */ 
    arm.it_value.tv_sec = 0; 
    arm.it_value.tv_nsec = 0L; 
    arm.it_interval.tv_sec = 0; 
    arm.it_interval.tv_nsec = 0L; 
    if (timer_settime(timeout_timer, 0, &arm, NULL)) 
     return errno; 

    return 0; 
} 

int timeout_done(void) 
{ 
    struct sigaction act; 
    struct itimerspec arm; 
    int    errors = 0; 

    /* Ignore the timeout signals. */ 
    sigemptyset(&act.sa_mask); 
    act.sa_handler = SIG_IGN; 
    if (sigaction(TIMEOUT_SIGNAL, &act, NULL)) 
     if (!errors) errors = errno; 

    /* Disarm any current timeouts. */ 
    arm.it_value.tv_sec = 0; 
    arm.it_value.tv_nsec = 0L; 
    arm.it_interval.tv_sec = 0; 
    arm.it_interval.tv_nsec = 0; 
    if (timer_settime(timeout_timer, 0, &arm, NULL)) 
     if (!errors) errors = errno; 

    /* Destroy the timer itself. */ 
    if (timer_delete(timeout_timer)) 
     if (!errors) errors = errno; 

    /* If any errors occurred, set errno. */ 
    if (errors) 
     errno = errors; 

    /* Return 0 if success, errno otherwise. */ 
    return errors; 
} 

記得在編譯時包括rt庫,即使用gcc -W -Wall *source*.c -lrt -o *binary*編譯。

的想法是,主程序首先調用timeout_init()安裝所有必要的處理等等,並且可以調用timeout_done()fork()之後,或者在一個子進程荷蘭國際集團)在退出之前deistall它。

要設置超時,你叫timeout_set(seconds)。返回值是一個超時描述符。目前只有一個標記可以使用timeout_passed()進行檢查,但超時信號的傳遞也會中斷任何阻塞的I/O調用。因此,您可以預計超時會中斷任何阻塞的I/O調用。

如果你想做的任何事情超過設定的超時一個標誌,你不能​​做到這一點的信號處理函數;請記住,在信號處理程序中,您僅限於使用異步信號安全功能。各地最簡單的方法是在sigwaitinfo()使用一個單獨的線程與一個死循環,以阻止所有其他線程中的TIMEOUT_SIGNAL信號。這樣專用線程可以保證捕捉信號,但同時不限於異步信號安全功能。例如,它可以做更多的工作,甚至可以使用pthread_kill()發送信號給特定的線程。 (只要該信號具有處理程序,即使是具有空體的處理程序,其交付也會中斷該線程中的任何阻塞I/O調用。)

以下是使用超時的簡單示例main()。這是愚蠢的,並依靠fgets()不重試(當被信號中斷),但它似乎工作。

#include <string.h> 
#include <stdio.h> 

int main(void) 
{ 
    char buffer[1024], *line; 
    int t1, t2, warned1; 

    if (timeout_init()) { 
     fprintf(stderr, "timeout_init(): %s.\n", strerror(errno)); 
     return 1; 
    } 

    printf("You have five seconds to type something.\n"); 
    t1 = timeout_set(2.5); warned1 = 0; 
    t2 = timeout_set(5.0); 
    line = NULL; 

    while (1) { 

     if (timeout_passed(t1)) { 
      /* Print only the first time we notice. */ 
      if (!warned1++) 
       printf("\nTwo and a half seconds left, buddy.\n"); 
     } 

     if (timeout_passed(t2)) { 
      printf("\nAw, just forget it, then.\n"); 
      break; 
     } 

     line = fgets(buffer, sizeof buffer, stdin); 
     if (line) { 
      printf("\nOk, you typed: %s\n", line); 
      break; 
     } 
    } 

    /* The two timeouts are no longer needed. */ 
    timeout_unset(t1); 
    timeout_unset(t2); 

    /* Note: 'line' is non-NULL if the user did type a line. */ 

    if (timeout_done()) { 
     fprintf(stderr, "timeout_done(): %s.\n", strerror(errno)); 
     return 1; 
    } 

    return 0; 
} 
+1

@Bylos:感謝您注意'timeout_unset()'中的錯誤。描述'狀態'標誌和超時定時器狀態改變意圖的評論真的很差,所以我也修正了這一點。 –

2

有用的閱讀是time(7)手冊頁。請注意,Linux還提供了timerfd_create(2)特定於Linux的syscall,經常與多路複用系統調用一起使用,如poll(2)(或ppoll(2)或較早的select(2)系統調用)。

如果你想使用的信號不要忘記仔細閱讀signal(7)手冊頁(大約有編碼信號處理程序的限制,你可能要設置一個volatile sigatomic_t變量在信號處理,你不應該做任何newdelete -or malloc & free - 信號處理程序,其中只有異步安全函數調用允許內部內存menagenment操作)。

還要注意,面向事件的編程(如GUI應用程序)通常提供在其事件循環中管理定時器的方法(在Qt中的Gtk中使用libevent,...)管理定時器。