c语言中的linux定时器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12764771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
timers in linux in c
提问by Bag Dev
Possible Duplicate:
Loops/timers in C
可能的重复:
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.
过去 3 天我一直在阅读有关计时器的信息,但找不到任何有用的东西,我试图在实际示例中理解它,有人可以帮助我弄清楚如何为以下程序设置警报。
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.
我如何设置一个计时器,以便它发送 2 个参数,一个是数组名称,第二个是要删除的数字,我知道下面的内容无论如何都不安全,我只是想了解如何使用带有 args 的警报以调用函数。
please note that the environment is Linux, and also I appreciate any link with a working C example.
请注意,环境是 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");
}
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
我想要做的是我有一个哈希值,它的值将在 1 秒后过期,所以在我将值插入哈希值后,我需要创建一个计时器,以便它在我们说之后删除该值1 秒,如果我在该间隔(1 秒)之前收到服务器的响应,然后我从哈希中删除该值并删除计时器,几乎就像在 tcp 中重传
采纳答案by Nominal Animal
Do you want to use signals or threads?
你想使用信号还是线程?
First, set up the signal handler or prepare a suitable thread function; see man 7 sigeventfor details.
首先,设置信号处理程序或准备一个合适的线程函数;有关详细信息,请参阅man 7 sigevent。
Next, create a suitable timer, using timer_create()
. See man 2 timer_createfor details.
接下来,创建一个合适的计时器,使用timer_create()
. 有关详细信息,请参阅man 2 timer_create。
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_settimefor details.
根据您在计时器触发时的操作,您可能希望将计时器设置为单次触发,或在之后的短时间内重复。您使用timer_settime()
既可布防又可撤防计时器;有关详细信息,请参阅man 2 timer_settime。
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.
在实际应用中,您通常需要复用定时器。即使一个进程可以创建多个计时器,它们也是有限的资源。尤其是超时计时器——它们很简单,可以设置标志和/或向特定线程发送信号——应该使用单个计时器,在下一次超时时触发,设置相关的超时标志,并可选择发送信号(使用空体处理程序)到所需的线程,以确保它被中断。(对于单线程进程,原始信号传递将中断阻塞的 I/O 调用。)考虑一个服务器,响应某个请求:在处理请求时,请求本身可能有大约一分钟左右的超时可能需要连接超时、I/O 超时等。
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.)
现在,最初的问题很有趣,因为定时器在有效使用时非常强大。但是,示例程序基本上是废话。为什么不创建一个设置一个或多个计时器的程序,例如每个计时器向标准输出输出一些东西?请记住使用write()
et al fromunistd.h
因为它们是异步信号安全的,而printf()
et cetera fromstdio.h
则不是。(如果您的信号处理程序使用非异步信号安全函数,则结果是未定义的。它通常有效,但根本不能保证;它可能与工作一样崩溃。测试不会告诉,因为它是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.)
(在法律允许的范围内,我将下面显示的代码片段的所有版权和相关和邻接权献给全球公共领域;请参阅CC0 公共领域奉献。换句话说,请随意以任何方式使用下面的代码希望,只是不要因为它的任何问题而责怪我。)
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.)
我使用了旧式 GCC 原子内置函数,因此它应该是线程安全的。添加一些内容后,它也应该适用于多线程代码。(例如,您不能使用互斥锁,因为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;
}
Remember to include the rt
library when compiling, i.e. use gcc -W -Wall *source*.c -lrt -o *binary*
to compile.
rt
编译时记得包含库,即用于gcc -W -Wall *source*.c -lrt -o *binary*
编译。
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).
这个想法是主程序首先调用timeout_init()
以安装所有必要的处理程序等,并且可能timeout_done()
在退出之前调用它(或在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.
要设置超时,请调用timeout_set(seconds)
。返回值是一个超时描述符。目前只有一个标志可以检查使用timeout_passed()
,但超时信号的传递也会中断任何阻塞的 I/O 调用。因此,您可以预期超时会中断任何阻塞 I/O 调用。
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.)
如果你想做的不仅仅是在超时时设置一个标志,你不能在信号处理程序中做;请记住,在信号处理程序中,您仅限于异步信号安全功能。解决这个问题的最简单方法是使用一个单独的线程,在 上无限循环sigwaitinfo()
,TIMEOUT_SIGNAL
信号在所有其他线程中被阻塞。这样可以保证专用线程捕获信号,但同时不限于异步信号安全功能。例如,它可以做更多的工作,甚至可以使用pthread_kill()
. (只要该信号有一个处理程序,即使是一个空体,它的传递将中断该线程中的任何阻塞 I/O 调用。)
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.
这是一个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;
}
回答by Basile Starynkevitch
A useful read is the time(7)man page. Notice that Linux also provides the timerfd_create(2)Linux specific syscall, often used with a multiplexing syscall like poll(2)(or ppoll(2)
or the older select(2)
syscall).
一个有用的阅读是time(7)手册页。请注意,Linux 还提供了timerfd_create(2)Linux 特定的系统调用,通常与poll(2)(或ppoll(2)
较旧的select(2)
系统调用)等多路复用系统调用一起使用。
If you want to use signals don't forget to read carefully signal(7)man page (there are restrictions about coding signal handlers; you might want to set a volatile sigatomic_t
variable in your signal handlers; you should not do any new
or delete
-or malloc
& free
- memory menagenment operations inside a signal handler, where only async-safefunction calls are permitted.).
如果您想使用信号,请不要忘记仔细阅读signal(7)手册页(编码信号处理程序有一些限制;您可能希望volatile sigatomic_t
在信号处理程序中设置一个变量;您不应该做 any new
or delete
-or malloc
& free
-信号处理程序内的内存管理操作,其中只允许异步安全函数调用。)。
Notice also that event-oriented programming, such as GUI applications, often provide ways (in Gtk, in Qt, with libevent
, ....) to manage timers in their event loop.
另请注意,面向事件的编程,例如 GUI 应用程序,通常提供方法(在 Gtk、Qt、with libevent
、....)在其事件循环中管理计时器。