Linux中的计时器c

时间:2012-10-06 23:29:18

标签: c linux timer

  

可能重复:
  Loops/timers in 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中的重传一样

2 个答案:

答案 0 :(得分:21)

您想使用信号还是线程?

首先,设置信号处理程序或准备合适的线程函数;有关详细信息,请参阅man 7 sigevent

接下来,使用timer_create()创建合适的计时器。有关详细信息,请参阅man 2 timer_create

根据计时器触发时的操作,您可能希望将计时器设置为单次计时,或者在之后的短时间间隔内重复计时。你使用timer_settime()同时武装和撤防计时器;有关详细信息,请参阅man 2 timer_settime

在实际应用中,您通常需要多路复用定时器。即使进程可以创建多个计时器,它们也是有限的资源。特别是超时定时器 - 无论是设置标志和/或向特定线程发送信号 - 都应使用单个定时器,该定时器在下一次超时时触发,设置相关的超时标志,并可选择发送信号(使用空体处理程序)到所需的线程,以确保它被中断。 (对于单线程进程,原始信号传递将中断阻塞I / O调用。)考虑服务器,响应某些请求:请求本身可能在一分钟左右的时间内超时,同时处理请求可能需要连接超时,I / O超时等。

现在,最初的问题很有意思,因为定时器在有效使用时功能强大。但是,示例程序基本上是无意义的。为什么不创建一个设置一个或多个计时器的程序,例如每个计时器输出一些标准输出?请务必使用write()中的unistd.hprintf(),而stdio.hpthread_mutex_lock()等等。 (如果你的信号处理程序使用非异步信号安全函数,结果是不确定的。它通常有效,但它根本不能保证;它也可能像工作一样崩溃。测试不会告诉,因为它是未定义。)


编辑添加:这是多路复用超时的简单示例。

(在法律允许的范围内,我将以下所示的代码段的所有版权及相关和相邻权利专门用于全球公共领域;请参阅async-signal safe。换句话说,请随意使用下面的代码无论如何,只要不要因为任何问题而责怪我。)

我使用旧式GCC原子内置函数,所以它应该是线程安全的。添加一些内容后,它也适用于多线程代码。 (你不能使用例如互斥锁,因为#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()来取消它(或者在{{1}之后的子进程中) }荷兰国际集团)。

要设置超时,请致电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 :(得分:2)

有用的阅读是time(7)手册页。请注意,Linux还提供timerfd_create(2)特定于Linux的syscall,通常与poll(2)(或ppoll(2)或较旧的select(2)系统调用)等多路复用系统调用一起使用。 / p>

如果您想使用信号,请不要忘记仔细阅读signal(7)手册页(编码信号处理程序有限制;您可能需要在信号处理程序中设置volatile sigatomic_t变量;您不应该在信号处理程序中执行任何newdelete - 或malloc&amp; free - 内存管理器操作,其中只有 async-safe 允许函数调用。)。

另请注意,面向事件的编程(例如GUI应用程序)通常提供方法(在Gtk中,在Qt中,使用libevent,....)来管理事件循环中的计时器。