【问题标题】:timers in linux in c [duplicate]c语言中的linux中的计时器[重复]
【发布时间】:2012-09-27 16:52:20
【问题描述】:

可能重复:
Loops/timers in C

过去 3 天我一直在阅读有关计时器的内容,但我找不到任何有用的东西,我正在尝试在实际示例中理解它,有人可以帮我弄清楚如何为下面设置警报程序。

我怎样才能设置一个定时器,让它发送2个参数,一个是数组名,第二个是要删除的数字,我知道下面的无论如何都不安全,我只是想了解如何使用带参数的警报来调用函数。

请注意,环境是 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 中的重新传输

【问题讨论】:

  • i&lt;sizeof( (sizeof(a)/sizeof(int)) ) 可能不是您想要的。那是i &lt; sizeof(size_t),我相信你想要i &lt; sizeof a / sizeof a[0]。同样在最后一个for 循环中。
  • 你可能想检查this问题
  • 简短的回答是您可以做到 - 但不完全是您拥有的方式。长答案是它是一个 long 答案,并且向您展示如何对其进行编码可能比大多数人想要在答案上投入更多的工作。根据您的具体操作方式,可能有六种选择。建议您查看create_timer/timer_settime/delete_timer 系列系统调用。 sigevent 中包含一个 sival_ptr,您可以使用它来指向一个包含要删除的数组 addr 和索引的结构。
  • 对不起。 timer_create/timer_delete.
  • @Duck:根据当前 man timer_create 手册页的 NOTES 部分,从 2.6.8 开始,它实际上受到 RLIMIT_SIGPENDING 的限制(使用 getrlimit(RLIMIT_SIGPENDING, &amp;rlim)struct rlimit rlim;)。内核可能会施加其他系统范围的限制,因此您不能真正依赖于在实践中获得那么多计时器。

标签: c linux timer


【解决方案1】:

你想使用信号还是线程?

首先,设置信号处理器或者准备一个合适的线程函数;详情请见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.hprintf() 等则不是。 (如果您的信号处理程序使用非异步信号安全函数,则结果未定义。它通常可以工作,但根本不能保证;它可能和工作一样崩溃。测试不会告诉你,因为它是 未定义。)


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

(在法律允许的范围内,我将下面显示的代码 sn-ps 的所有版权以及相关和邻接权奉献给全球公共领域;请参阅CC0 Public Domain Dedication。换句话说,请随意使用下面的代码以任何你希望的方式,只是不要因为它的任何问题而责怪我。)

我使用了老式的 GCC atomic built-ins,所以它应该是线程安全的。通过一些添加,它也应该适用于多线程代码。 (例如,您不能使用互斥锁,因为 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()ing 之后的子进程中)。

要设置超时,请致电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;
}

【讨论】:

  • @Bylos:感谢您注意到timeout_unset() 中的错误。描述 state 标志和超时计时器状态更改意图的评论真的很糟糕,所以我也修复了它。
【解决方案2】:

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

如果你想使用信号,别忘了仔细阅读 signal(7) 手册页(关于编码信号处理程序有限制;你可能想在你的信号处理程序中设置一个 volatile sigatomic_t 变量;你不应该做任何事情newdelete - 或 malloc & free- 信号处理程序内的内存管理操作,其中仅允许 async-safe 函数调用。)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 2013-01-23
    • 1970-01-01
    • 1970-01-01
    • 2021-12-11
    相关资源
    最近更新 更多