c 中的 linux 中的计时器 [英] timers in linux in c

查看:23
本文介绍了c 中的 linux 中的计时器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

<块引用>

可能的重复:
C 中的循环/计时器

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

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

请注意,环境是 Linux,我也感谢任何与工作 C 示例的链接.

#includeint delete_from_array(int arg) ;int main(){int a[10000], i, y ;//这里如何设置定时器以在半秒后删除数组中的任何数字对于 (y=0; y <100; y++) {for (i=0; inumber;for (i=0; i

我想要做的是我有一个哈希值,它的值将在 1 秒后过期,所以在我将值插入哈希值后,我需要创建一个计时器,以便它删除该值假设 1 秒后,如果我在该间隔(1 秒)之前收到服务器的响应,那么我从哈希中删除该值并删除计时器,几乎就像在 tcp 中重传

解决方案

你想使用信号还是线程?

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

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

根据您在计时器触发时执行的操作,您可能希望将计时器设置为单次触发,或在之后的短时间内重复.您使用 timer_settime() 来布防和撤防定时器;有关详细信息,请参阅 man 2 timer_settime.

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

现在,最初的问题很有趣,因为定时器在有效使用时非常强大.但是,示例程序基本上是废话.为什么不创建一个设置一个或多个计时器的程序,例如每个计时器向标准输出输出一些东西?请记住使用 unistd.h 中的 write() 等,因为它们是 异步信号安全,而 printf() 等来自 stdio.h 不是.(如果您的信号处理程序使用非异步信号安全函数,则结果是未定义的.它通常有效,但根本不能保证;它可能与工作一样崩溃.测试不会告诉,因为它是 未定义.)

<小时>

编辑添加:这是多路复用超时的基本示例.

(在法律允许的范围内,我将下面显示的代码片段的所有版权以及相关和邻接权献给全球公共领域;参见 CC0 Public Domain Dedication.换句话说,您可以随意使用下面的代码,只要您有任何问题,请不要怪我.)

我使用了旧式 GCC 原子内置函数,因此它应该是线程安全的.添加一些内容后,它也应该适用于多线程代码.(例如,您不能使用互斥锁,因为 pthread_mutex_lock() 不是异步信号安全的.以原子方式操作超时状态应该可以工作,尽管如果您在它触发时禁用超时,可能会留下一些竞争.)

#define _POSIX_C_SOURCE 200809L#include #include #include #include #define 超时 16#define TIMEOUT_SIGNAL (SIGRTMIN+0)#define TIMEOUT_USED 1#define TIMEOUT_ARMED 2#define TIMEOUT_PASSED 4静态 timer_t timeout_timer;静态易失性 sig_atomic_t timeout_state[TIMEOUTS] = { 0 };静态结构 timespec timeout_time[TIMEOUTS];/* 返回之前和之后之间的秒数,(之后 - 之前).* 这必须是异步信号安全的,所以它不能使用 difftime().*/static inline double timespec_diff(const struct timespec after, const struct timespec before){返回(双)(after.tv_sec - before.tv_sec)+ (double)(after.tv_nsec - before.tv_nsec)/1000000000.0;}/* 将正秒数添加到时间规范,如果秒数为负数,则什么都没有.* 这必须是异步信号安全的.*/static inline void timespec_add(struct timespec *const to, const double seconds){如果(到&&秒> 0.0){long s =(长)秒;long ns = (long)(0.5 + 1000000000.0 * (seconds - (double)s));/* 调整舍入误差.*/如果 (ns <0L)ns = 0L;别的如果 (ns > 999999999L)纳秒 = 999999999L;to->tv_sec += (time_t)s;to->tv_nsec += ns;如果 (to->tv_nsec >= 1000000000L) {to->tv_nsec -= 1000000000L;to->tv_sec++;}}}/* 将 timespec 设置为指定的秒数,如果为负秒,则为零.*/static inline void timespec_set(struct timespec *const to, const double seconds){如果(到){如果(秒 > 0.0){const long s = (long)seconds;long ns = (long)(0.5 + 1000000000.0 * (seconds - (double)s));如果 (ns <0L)ns = 0L;别的如果 (ns > 999999999L)纳秒 = 999999999L;to->tv_sec = (time_t)s;to->tv_nsec = ns;} 别的 {to->tv_sec = (time_t)0;to->tv_nsec = 0L;}}}/* 如果发生超时,则返回非零值.*/静态内联 int timeout_passed(const int timeout){如果(超时> = 0&&超时< TIMEOUTS){const int state = __sync_or_and_fetch(&timeout_state[timeout], 0);/* 指的是一个未使用的超时?*/if (!(state & TIMEOUT_USED))返回-1;/* 没有武装?*/if (!(state & TIMEOUT_ARMED))返回-1;/* 如果超时,则返回 1,否则返回 0.*/返回(状态 & TIMEOUT_PASSED)?1:0;} 别的 {/* 无效的超时号码.*/返回-1;}}/* 解除超时.* 如果超时尚未触发,则返回 0,如果已触发则返回 1.*/静态内联 int timeout_unset(const int timeout){如果(超时> = 0&&超时< TIMEOUTS){/* 获取当前超时状态为'state',* 然后清除除 TIMEOUT_PASSED 标志之外的所有内容* 对于指定的超时.* 感谢 Bylos 捕获此错误.*/const int state = __sync_fetch_and_and(&timeout_state[timeout], TIMEOUT_PASSED);/* 超时无效?*/if (!(state & TIMEOUT_USED))返回-1;/* 没有武装?*/if (!(state & TIMEOUT_ARMED))返回-1;/* 如果通过则返回 1,否则返回 0.*/返回(状态 & TIMEOUT_PASSED)?1:0;} 别的 {/* 无效的超时号码.*/返回-1;}}int timeout_set(常量双秒){struct timespec 现在,然后;struct itimerspec 何时;双下一个;int 超时,我;/* 超时必须在未来.*/如果(秒 <= 0.0)返回-1;/* 获取当前时间, */如果 (clock_gettime(CLOCK_REALTIME, &now))返回-1;/* 并计算超时应该触发的时间.*/然后=现在;timespec_add(&then, 秒);/* 找到一个未使用的超时.*/for (timeout = 0; timeout < TIMEOUTS; timeout++)如果 (!(__sync_fetch_and_or(&timeout_state[timeout], TIMEOUT_USED) & TIMEOUT_USED))休息;/* 没有未使用的超时?*/如果(超时 >= 超时)返回-1;/* 从状态中清除除 TIMEOUT_USED 之外的所有内容,*/__sync_and_and_fetch(&timeout_state[timeout], TIMEOUT_USED);/* 更新超时细节,*/timeout_time[超时] = 然后;/* 并将超时标记为可设置.*/__sync_or_and_fetch(&timeout_state[timeout], TIMEOUT_ARMED);/* 距离下一次超时还有多久?*/下一个 = 秒;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)下一个 = 秒;}/* 计算下一次触发超时的持续时间,*/timespec_set(&when.it_value, next);when.it_interval.tv_sec = 0;when.it_interval.tv_nsec = 0L;/* 并启动定时器.*/if (timer_settime(timeout_timer, 0, &when, NULL)) {/* 失败的.*/__sync_and_and_fetch(&timeout_state[timeout], 0);返回-1;}/* 返回超时数.*/返回超时;}static void timeout_signal_handler(int signum __attribute__((unused)), siginfo_t *info, void *context __attribute__((unused))){struct timespec 现在;struct itimerspec 何时;int saved_errno, i;双下一个;/* 不是定时器信号?*/if (!info || info->si_code != SI_TIMER)返回;/* 保存错误号;使用的某些函数可能会修改 errno.*/保存的错误号 = 错误号;如果 (clock_gettime(CLOCK_REALTIME, &now)) {错误号 = 保存的错误号;返回;}/* 假设没有下一次超时.*/下一个 = -1.0;/* 检查所有已使用和已设置但尚未通过的超时.*/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);如果(秒 <= 0.0){/* 超时 [i] 触发!*/__sync_or_and_fetch(&timeout_state[i], TIMEOUT_PASSED);} 别的if (next <= 0.0 || seconds < next) {/* 这是未来最快的超时.*/下一个 = 秒;}}/* 注意:如果 next <= 0.0,timespec_set() 会将时间设置为零,* 这反过来将解除定时器.* 定时器是一次性的;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);/* 恢复错误号.*/错误号 = 保存的错误号;}int timeout_init(void){struct sigaction 行为;结构信号事件;struct itimerspec 臂;/* 安装 timeout_signal_handler.*/sigemptyset(&act.sa_mask);act.sa_sigaction = timeout_signal_handler;act.sa_flags = SA_SIGINFO;if (sigaction(TIMEOUT_SIGNAL, &act, NULL))返回错误号;/* 创建一个将向 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))返回错误号;/* 解除超时定时器(暂时).*/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))返回错误号;返回0;}int timeout_done(void){struct sigaction 行为;struct itimerspec 臂;int 错误 = 0;/* 忽略超时信号.*/sigemptyset(&act.sa_mask);act.sa_handler = SIG_IGN;if (sigaction(TIMEOUT_SIGNAL, &act, NULL))如果(!错误)错误 = 错误号;/* 解除任何当前超时.*/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))如果(!错误)错误 = 错误号;/* 销毁定时器本身.*/如果(定时器删除(超时定时器))如果(!错误)错误 = 错误号;/* 如果发生任何错误,设置 errno.*/如果(错误)错误号 = 错误;/* 如果成功则返回 0,否则返回 errno.*/返回错误;}

编译时记得包含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 #include int main(void){字符缓冲区[1024],*行;int t1, t2, 警告 1;如果(超时_init()){fprintf(stderr, "timeout_init(): %s.
", strerror(errno));返回 1;}printf("你有五秒钟的时间输入内容.
");t1 = timeout_set(2.5);警告 1 = 0;t2 = timeout_set(5.0);行 = NULL;而 (1) {如果(超时通过(t1)){/* 只打印我们第一次注意到的时候.*/如果(!警告1++)printf("
还有两秒半,伙计.
");}如果(超时时间(t2)){printf("
哦,那就算了吧.
");休息;}line = fgets(buffer, sizeof buffer, stdin);如果(行){printf("
好的,你输入了:%s
", line);休息;}}/* 不再需要两次超时.*/timeout_unset(t1);timeout_unset(t2);/* 注意:如果用户确实输入了一行,'line' 是非 NULL.*/如果(超时_完成()){fprintf(stderr, "timeout_done(): %s.
", strerror(errno));返回 1;}返回0;}

Possible Duplicate:
Loops/timers in C

I've been reading about timers for the last 3 days and I'm unable to find anything useful, I'm trying to understand it in real example, can somebody help me figure out how to setup an alarm for the below program.

How can I set a a timer so that it will send 2 args, one is the array name, and the second one is the number to be deleted, I know the below is not safe in anyway, I'm just trying to understand how use alarms with args to call a function.

please note that the environment is Linux, and also I appreciate any link with a working C example.

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

    }

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

    }

What I'm trying to do is that I have a hash which has has values to be expired after 1 seconds, so after I insert the value into the hash, I need to create a timer so that it will delete that value after let's say 1 second, and IF I got a response from the server before the that interval (1 second) then I delete the value from the hash and delete the timer, almost like retransmission in tcp

解决方案

Do you want to use signals or threads?

First, set up the signal handler or prepare a suitable thread function; see man 7 sigevent for details.

Next, create a suitable timer, using timer_create(). See man 2 timer_create for details.

Depending on what you do when the timer fires, you may wish to set the timer to either one-shot, or to repeat at a short interval afterwards. You use timer_settime() to both arm, and to disarm, the timer; see man 2 timer_settime for details.

In practical applications you usually need to multiplex the timer. Even though a process can create multiple timers, they are a limited resource. Especially timeout timers -- which are trivial, either setting a flag and/or sending a signal to a specific thread -- should use a single timer, which fires at the next timeout, sets the related timeout flag, and optionally send a signal (with an empty-body handler) to the desired thread to make sure it is interrupted. (For a single-thread process, the original signal delivery will interrupt blocking I/O calls.) Consider a server, responding to some request: the request itself might have a timeout on the order of a minute or so, while processing the request might need connection timeouts, I/O timeouts, and so on.

Now, the original question is interesting, because timers are powerful when used effectively. However, the example program is basically nonsense. Why don't you create say a program that sets one or more timers, each for example outputting something to standard output? Remember to use write() et al from unistd.h as they are async-signal safe, whereas printf() et cetera from stdio.h are not. (If your signal handlers use non-async-signal safe functions, the results are undefined. It usually works, but it's not guaranteed at all; it may just as well crash as work. Testing will not tell, as it is undefined.)


Edited to add: Here is a bare-bones example of multiplexed timeouts.

(To the extent possible under law, I dedicate all copyright and related and neighboring rights to the code snippets shown below to the public domain worldwide; see CC0 Public Domain Dedication. In other words, feel free to use the code below in any way you wish, just don't blame me for any problems with it.)

I used old-style GCC atomic built-ins, so it should be thread-safe. With a few additions, it should work for multithreaded code too. (You cannot use for example mutexes, because pthread_mutex_lock() is not async-signal safe. Atomically manipulating the timeout states should work, although there might be some races left if you disable a timeout just when it fires.)

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

Remember to include the rt library when compiling, i.e. use gcc -W -Wall *source*.c -lrt -o *binary* to compile.

The idea is that the main program first calls timeout_init() to install all the necessary handlers et cetera, and may call timeout_done() to deistall it before exiting (or in a child process after fork()ing).

To set a timeout, you call timeout_set(seconds). The return value is a timeout descriptor. Currently there is just a flag you can check using timeout_passed(), but the delivery of the timeout signal also interrupts any blocking I/O calls. Thus, you can expect the timeout to interrupt any blocking I/O call.

If you want to do anything more than set a flag at timeout, you cannot do it in the signal handler; remember, in a signal handler, you're limited to async-signal safe functions. The easiest way around that is to use a separate thread with an endless loop over sigwaitinfo(), with the TIMEOUT_SIGNAL signal blocked in all other threads. That way the dedicated thread is guaranteed to catch the signal, but at the same time, is not limited to async-signal safe functions. It can, for example, do much more work, or even send a signal to a specific thread using pthread_kill(). (As long as that signal has a handler, even one with an empty body, its delivery will interrupt any blocking I/O call in that thread.)

Here is a simple example main() for using the timeouts. It is silly, and relies on fgets() not retrying (when interrupted by a signal), but it seems to work.

#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.
", strerror(errno));
        return 1;
    }

    printf("You have five seconds to type something.
");
    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("
Two and a half seconds left, buddy.
");
        }

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

        line = fgets(buffer, sizeof buffer, stdin);
        if (line) {
            printf("
Ok, you typed: %s
", 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.
", strerror(errno));
        return 1;
    }

    return 0;
}

这篇关于c 中的 linux 中的计时器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆