2012-10-06 32 views
5

thể trùng lặp:
Loops/timers in Cgiờ trong linux trong c

Tôi đã đọc về giờ trong 3 ngày qua và tôi không thể tìm thấy bất cứ điều gì hữu ích, tôi m cố gắng để hiểu nó trong ví dụ thực tế, ai đó có thể giúp tôi tìm ra cách để thiết lập một báo động cho các chương trình dưới đây.

Làm cách nào tôi có thể đặt hẹn giờ để nó sẽ gửi 2 arg, một là tên mảng và thứ hai là số bị xóa, tôi biết dưới đây không an toàn, tôi chỉ đang cố gắng để hiểu cách sử dụng báo động với args để gọi một hàm.

xin lưu ý rằng môi trường là Linux và tôi cũng đánh giá cao bất kỳ liên kết nào có ví dụ C hoạt động.

#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"); 

    } 

Những gì tôi đang cố gắng làm là rằng tôi có một băm mà đã có giá trị được hết hạn sau 1 giây, vì vậy sau khi tôi chèn giá trị vào băm, tôi cần phải tạo ra một bộ đếm thời gian để nó sẽ xóa giá trị đó sau khi giả sử 1 giây và NẾU tôi nhận được phản hồi từ máy chủ trước khoảng thời gian đó (1 giây) thì tôi xóa giá trị khỏi băm và xóa bộ hẹn giờ, gần giống như truyền lại ở tcp

+0

Điều này 'i

+2

Bạn có thể muốn kiểm tra [this] (http://stackoverflow.com/questions/5540245/loops-timers-in-c) câu hỏi – tomahh

+0

sai lầm của tôi, tôi có nghĩa là sizeof (a)/sizeof (int), tôi sẽ sửa đổi mã. –

Trả lời

19

bạn muốn sử dụng tín hiệu hoặc chủ đề?

Trước tiên, hãy thiết lập bộ xử lý tín hiệu hoặc chuẩn bị chức năng chỉ phù hợp; xem man 7 sigevent để biết chi tiết.

Tiếp theo, tạo bộ hẹn giờ phù hợp, sử dụng timer_create(). Xem man 2 timer_create để biết chi tiết.

Tùy thuộc vào những gì bạn làm khi bộ hẹn giờ kích hoạt, bạn có thể muốn đặt hẹn giờ thành một lần hoặc lặp lại sau một khoảng thời gian ngắn. Bạn sử dụng timer_settime() cho cả hai cánh tay và để giải giáp vũ khí, bộ hẹn giờ; xem man 2 timer_settime để biết chi tiết.

Trong các ứng dụng thực tế, bạn thường cần phải ghép giờ. Mặc dù một quá trình có thể tạo nhiều bộ tính giờ, nhưng chúng là một tài nguyên giới hạn. Đặc biệt là thời gian chờ - không quan trọng, hoặc đặt cờ và/hoặc gửi tín hiệu đến một chuỗi cụ thể - nên sử dụng một bộ hẹn giờ duy nhất, sẽ kích hoạt vào thời gian chờ tiếp theo, đặt cờ thời gian chờ có liên quan và tùy chọn gửi tín hiệu (với một bộ xử lý rỗng) đến chuỗi mong muốn để đảm bảo nó bị gián đoạn. (Đối với một quá trình đơn luồng, việc gửi tín hiệu ban đầu sẽ làm gián đoạn các cuộc gọi I/O chặn.) Xem xét một máy chủ, trả lời một số yêu cầu: yêu cầu có thể có thời gian chờ trên một phút hoặc lâu hơn, trong khi xử lý yêu cầu có thể cần thời gian chờ kết nối, thời gian chờ I/O, v.v.

Bây giờ, câu hỏi ban đầu là thú vị, bởi vì bộ hẹn giờ mạnh mẽ khi được sử dụng hiệu quả. Tuy nhiên, chương trình ví dụ về cơ bản là vô nghĩa. Tại sao bạn không tạo ra một chương trình đặt một hoặc nhiều bộ tính giờ, mỗi ví dụ đưa ra một cái gì đó cho đầu ra tiêu chuẩn? Hãy nhớ sử dụng write() et al từ unistd.h vì chúng là async-signal safe, trong khi printf() et cetera từ stdio.h thì không. (Nếu trình xử lý tín hiệu của bạn sử dụng các chức năng an toàn không đồng bộ, kết quả là không xác định. Nó thường hoạt động, nhưng nó không được đảm bảo chút nào; nó cũng có thể bị hỏng như công việc. .)


Chỉnh sửa để thêm: Đây là ví dụ hoàn hảo về thời gian chờ ghép kênh.

(Trong phạm vi có thể theo luật pháp, tôi dành tất cả quyền bản quyền và quyền liên quan và lân cận cho các đoạn mã được hiển thị bên dưới vào phạm vi công cộng trên toàn thế giới; xem CC0 Public Domain Dedication. Nói cách khác, vui lòng sử dụng mã bên dưới theo bất kỳ cách nào bạn muốn, đừng đổ lỗi cho tôi vì bất kỳ vấn đề nào với nó.)

Tôi đã sử dụng các trình xây dựng nguyên tử GCC kiểu cũ, vì vậy nó phải an toàn chỉ. Với một vài bổ sung, nó cũng làm việc cho mã đa luồng. (Bạn không thể sử dụng ví dụ mutexes, vì pthread_mutex_lock() là không an toàn async-tín hiệu. Atomically thao tác các tiểu bang timeout nên làm việc, mặc dù có thể có một số cuộc đua trái nếu bạn vô hiệu hóa một thời gian chờ chỉ khi nó cháy.)

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

Hãy nhớ bao gồm thư viện rt khi biên dịch, tức là sử dụng gcc -W -Wall *source*.c -lrt -o *binary* để biên dịch.

Ý tưởng là chương trình chính đầu tiên gọi timeout_init() để cài đặt tất cả các trình xử lý cần thiết và cetera, và có thể gọi timeout_done() để deistall nó trước khi thoát (hoặc trong một quá trình con sau fork() ing).

Để đặt thời gian chờ, bạn gọi timeout_set(seconds). Giá trị trả về là một bộ mô tả thời gian chờ. Hiện tại chỉ có một lá cờ bạn có thể kiểm tra bằng cách sử dụng timeout_passed(), nhưng việc phân phối tín hiệu thời gian chờ cũng làm gián đoạn bất kỳ cuộc gọi I/O chặn nào. Vì vậy, bạn có thể mong đợi thời gian chờ để ngắt bất kỳ cuộc gọi I/O chặn nào.

Nếu bạn muốn làm bất cứ điều gì hơn là đặt cờ vào thời gian chờ, bạn không thể thực hiện điều đó trong trình xử lý tín hiệu; hãy nhớ, trong một trình xử lý tín hiệu, bạn bị giới hạn các chức năng an toàn không đồng bộ-tín hiệu. Cách dễ nhất xung quanh đó là sử dụng một sợi riêng biệt với vòng lặp vô tận trên sigwaitinfo(), với tín hiệu TIMEOUT_SIGNAL bị chặn trong tất cả các chuỗi khác. Bằng cách đó, các sợi chuyên dụng được đảm bảo để bắt tín hiệu, nhưng đồng thời, không giới hạn chức năng an toàn không đồng bộ tín hiệu. Nó có thể, ví dụ, làm nhiều việc hơn, hoặc thậm chí gửi một tín hiệu đến một sợi cụ thể bằng cách sử dụng pthread_kill(). (Miễn là tín hiệu đó có một bộ xử lý, ngay cả một với một cơ thể trống, giao hàng của nó sẽ làm gián đoạn bất kỳ cuộc gọi I/O chặn trong chủ đề đó.)

Đây là một ví dụ đơn giản main() để sử dụng hết thời gian chờ. Nó là ngớ ngẩn, và dựa trên fgets() không thử lại (khi bị gián đoạn bởi một tín hiệu), nhưng có vẻ như nó hoạt động.

#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: Cảm ơn bạn đã chú ý đến lỗi trong 'timeout_unset()'. Nhận xét mô tả ý định của trạng thái của trạng thái bộ đếm thời gian và cờ 'trạng thái 'đã thực sự kém, vì vậy tôi cũng đã sửa nó. –

2

Đọc hữu ích là trang người đàn ông time(7). Lưu ý rằng Linux cũng cung cấp timerfd_create(2) Linux cụ thể syscall, thường được sử dụng với một syscall ghép kênh như poll(2) (hoặc ppoll(2) hoặc cũ hơn select(2) syscall).

Nếu bạn muốn sử dụng tín hiệu đừng quên đọc kỹ signal(7) trang người đàn ông (có những hạn chế về mã hóa xử lý tín hiệu, bạn có thể muốn thiết lập một biến volatile sigatomic_t trong xử lý tín hiệu của bạn, bạn không nên làm bất cứ new hoặc delete -hoặc malloc & free - bộ nhớ hoạt động menagenment bên trong trình xử lý tín hiệu, trong đó chỉ cho phép async-safe chức năng.).

Cũng lưu ý rằng lập trình hướng sự kiện, chẳng hạn như ứng dụng GUI, thường cung cấp các cách (trong Gtk, trong Qt, với libevent, ....) để quản lý bộ tính giờ trong vòng lặp sự kiện của chúng.