2012-10-06 6 views
5

Powiel możliwe:
Loops/timers in Ctimerów Linux c

Czytałem o timerów w ciągu ostatnich 3 dni i jestem w stanie znaleźć coś użyteczne, I” Próbuję to zrozumieć na prawdziwym przykładzie, czy ktoś może mi pomóc dowiedzieć się, jak ustawić alarm dla poniższego programu.

Jak ustawić timer tak, aby wysyłał 2 argumenty, jedna to nazwa tablicy, a druga to numer do usunięcia, wiem, że poniżej nie jest bezpiecznie, po prostu próbuję aby zrozumieć, w jaki sposób używać alarmów z argumentami, aby wywołać funkcję.

Należy pamiętać, że środowisko to Linux, a także doceniam każdy link z działającym przykładem 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"); 

    } 

Co staram się robić to, że mam hash, która ma wartości należy wygasła po 1 sekundy, więc po włożeniu do wartości hash, muszę stworzyć czasomierz tak, że usunie tę wartość po powiedzmy 1 sekundę, i JEŚLI otrzymałem odpowiedź z serwera przed tym interwałem (1 sekunda), wtedy usunę wartość z hasha i usuń licznik czasu, prawie jak retransmisja w tcp

+0

To 'i

+2

Możesz sprawdzić [to] (http://stackoverflow.com/questions/5540245/loops-timers-in-c) pytanie – tomahh

+0

mój błąd, miałem na myśli sizeof (a)/sizeof (int), będę zmodyfikuj kod. –

Odpowiedz

19

chcesz użyć sygnałów lub wątków?

Najpierw skonfiguruj procedurę obsługi sygnału lub przygotuj odpowiednią funkcję gwintowania; patrz: man 7 sigevent w celu uzyskania szczegółowych informacji.

Następnie utwórz odpowiedni zegar, używając timer_create(). Aby uzyskać szczegółowe informacje, patrz man 2 timer_create.

W zależności od tego, co zrobisz, gdy timer się uruchomi, możesz ustawić timer na jedno zdjęcie lub powtórzyć po krótkiej przerwie. Używasz timer_settime() do uzbrojenia i do rozbrojenia timera; patrz: man 2 timer_settime w celu uzyskania szczegółowych informacji.

W zastosowaniach praktycznych zwykle konieczne jest zwielokrotnienie licznika czasu. Mimo że proces może tworzyć wiele liczników czasu, są one ograniczonym zasobem. Zwłaszcza timery Timeout - które są trywialne, ustawiając flagę i/lub wysyłając sygnał do określonego wątku - powinny wykorzystywać pojedynczy timer, który uruchamia się przy następnym timeoutie, ustawia odpowiednią flagę timeout i opcjonalnie wysyła sygnał (z pustym treserem) do żądanego wątku, aby upewnić się, że jest przerwany. (W przypadku procesu z jednym gwintem pierwotne dostarczenie sygnału przerwie blokowanie wywołań We/Wy.) Rozważ serwer, odpowiadający na pewne żądanie: samo żądanie może mieć limit czasu rzędu minuty lub mniej, podczas przetwarzania żądania może wymagać limitu czasu połączenia, limitów czasu we/wy i tak dalej.

Teraz oryginalne pytanie jest interesujące, ponieważ zegary są potężne, gdy są skutecznie wykorzystywane. Jednak przykładowy program jest po prostu nonsensem. Dlaczego nie stworzysz programu, który ustawia jeden lub więcej timerów, każdy na przykład wyprowadzając coś na standardowe wyjście? Pamiętaj, aby używać write() i innych z , ponieważ są one async-signal safe, natomiast printf() i tak dalej od stdio.h nie są. (Jeśli twoje programy obsługi sygnału używają funkcji bezpiecznych bez sygnału asynchronicznego, wyniki są nieokreślone, zwykle działa, ale nie jest gwarantowane, może równie dobrze działać jak awaria, testowanie nie powie, ponieważ jest to niezdefiniowana .)


Edytowane w celu dodania: Oto prosty przykład zmultipleksowanych limitów czasu.

(W zakresie dozwolonym przez prawo, dedykuję wszystkie prawa autorskie oraz pokrewne i sąsiadujące prawa do fragmentów kodu pokazanych poniżej w domenie publicznej na całym świecie, patrz CC0 Public Domain Dedication. Innymi słowy, możesz użyć poniższego kodu w jakikolwiek sposób chcesz, po prostu nie obwiniaj mnie za jakiekolwiek problemy z tym.)

Użyłem wbudowanych atomików GCC w starym stylu, więc powinno to być bezpieczne dla wątków. Z kilkoma dodatkami powinno działać również dla kodu wielowątkowego. (Nie można użyć na przykład muteksy, ponieważ pthread_mutex_lock() nie jest bezpieczne asynchroniczny sygnał. Atomowo manipulowania stany limitu czasu powinno działać, choć nie mogą być pewne rasy lewo jeśli wyłączysz timeout tylko kiedy to pożary.)

#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; 
} 

Pamiętaj, aby podczas kompilacji dołączyć bibliotekę rt, tj. Użyć kompilacji gcc -W -Wall *source*.c -lrt -o *binary*.

Chodzi o to, że główny program najpierw wywołuje timeout_init() zainstalować wszystkie potrzebne programy obsługi i tak dalej, i może wywołać timeout_done() do deistall go przed zamknięciem (lub w procesie potomnym po fork() ing). Aby uzyskać limit czasu, zadzwoń pod numer timeout_set(seconds). Wartość zwracana jest deskryptorem limitu czasu. Obecnie jest tylko flaga, którą możesz sprawdzić, używając timeout_passed(), ale dostarczenie sygnału timeout również przerywa wszelkie blokujące połączenia I/O. W ten sposób można oczekiwać, że limit czasu przerwie wszelkie blokujące wywołania we/wy.

Jeśli chcesz zrobić coś więcej niż ustawić flagę na timeout, nie możesz tego zrobić w procedurze obsługi sygnału; pamiętaj, że w procederze obsługi sygnału jesteś ograniczony do bezpiecznych funkcji asynchronicznych. Najłatwiejszym sposobem jest użycie oddzielnego wątku z nieskończoną pętlą ponad sigwaitinfo(), z sygnałem TIMEOUT_SIGNAL zablokowanym we wszystkich innych wątkach. W ten sposób zagwarantowany jest specjalny wątek, który wychwytuje sygnał, ale jednocześnie nie jest ograniczony do funkcji bezpiecznego sygnału asynchronicznego. Może na przykład wykonać znacznie więcej pracy, a nawet wysłać sygnał do określonego wątku za pomocą pthread_kill(). (Dopóki ten sygnał ma uchwyt, nawet taki z pustym ciałem, jego dostarczenie przerwie wszelkie blokujące wywołania we/wy w tym wątku).

Oto prosty przykład: main() do korzystania z limitów czasu. To jest głupie, i polega na fgets() nie ponawiają próby (gdy przerywany przez sygnał), ale wydaje się działać.

+1

@Bylos: Dzięki za zauważenie błędu w 'timeout_unset()'. Komentarz opisujący intencję flagi "state" i zmiany czasu timera timera był naprawdę słaby, więc to naprawiłem. –

2

Przydatny odczyt to strona man time(7). Zauważ, że Linux zapewnia również timerfd_create(2) specyficzne dla systemu Linux syscall, często używane z multipleksową linią systemową, taką jak poll(2) (lub ppoll(2) lub starszą wersją systemu select(2)).

Jeśli chcesz korzystać z sygnałów nie zapomnij przeczytać uważnie signal(7) stronę man (istnieją ograniczenia dotyczące kodowania obsługi sygnałów, może chcesz ustawić zmienną volatile sigatomic_t w twoich obsługi sygnałów, że nie powinien robić każdy new lub delete lub malloc & free - operacje związane z zarządzaniem pamięcią wewnątrz programu obsługi sygnału, w którym dozwolone są tylko wywołania funkcji asynchronicznie bezpieczne.).

Należy również zauważyć, że programowanie zorientowane na zdarzenia, takie jak aplikacje GUI, często zapewnia sposoby (w Gtk, w Qt, z libevent, ....) do zarządzania zegarami w ich pętli zdarzeń.