在c(pthreads)中,获得线程紧定时的最佳方法是什么
好的,我有一个线程,我需要每10毫秒运行一次,但是它需要不同的处理时间(为了简单起见,我们可以假设处理时间小于10毫秒)。随着时间的推移,时间上的微小偏差会累积起来并成为一个问题 这是我目前的解决方案。它看起来很笨重,但更重要的是,我担心运行timeval_subtract所需的时间会导致计时错误。有谁有更好的解决方案吗 这是一个库,我不能使用系统资源,如计时器或时钟在c(pthreads)中,获得线程紧定时的最佳方法是什么,c,linux,pthreads,C,Linux,Pthreads,好的,我有一个线程,我需要每10毫秒运行一次,但是它需要不同的处理时间(为了简单起见,我们可以假设处理时间小于10毫秒)。随着时间的推移,时间上的微小偏差会累积起来并成为一个问题 这是我目前的解决方案。它看起来很笨重,但更重要的是,我担心运行timeval_subtract所需的时间会导致计时错误。有谁有更好的解决方案吗 这是一个库,我不能使用系统资源,如计时器或时钟 void mythread(void *ptr ) { struct timeval tv_being, tv_end;
void mythread(void *ptr )
{
struct timeval tv_being, tv_end;
int usToSleep;
while(1)
{
gettimeofday(&tv_begin)
//do stuff
//now determine how long to sleep so we wake up 10ms after previous wakeup
gettimeofday(&tv_end)
usToSleep = timeval_subtract(tv_begin, tv_end); //this will return 10ms minus the elapsed time
usleep(usToSleep);
}
return;
}
在存储时间的线程开始时使用gettimeofday()
在线程末尾使用gettimeofday()
计算差异=仅为估计值
gettimeofday()
为您提供微秒的时间,这是您可以获得的最短时间
基本用法:
void millitime(){/*secondsSinceEpoch*/
struct timeval tv;
gettimeofday(&tv, NULL);
printf("secs:%Ld usecs:%Ld \n",(unsigned long long)(tv.tv_sec),(unsigned long long)(tv.tv_usec));
}
在存储时间的线程开始时使用gettimeofday()
在线程末尾使用gettimeofday()
计算差异=仅为估计值
gettimeofday()
为您提供微秒的时间,这是您可以获得的最短时间
基本用法:
void millitime(){/*secondsSinceEpoch*/
struct timeval tv;
gettimeofday(&tv, NULL);
printf("secs:%Ld usecs:%Ld \n",(unsigned long long)(tv.tv_sec),(unsigned long long)(tv.tv_usec));
}
一天中的男人
符合
POSIX.1-2008将gettimeofday()标记为过时,建议使用clock_get-time(2)
此外,您可能希望多次运行测试,并保持平均运行时间更准确。man gettimeofday
符合
POSIX.1-2008将gettimeofday()标记为过时,建议使用clock_get-time(2)
此外,您可能希望多次运行测试,并保持平均运行时间更准确。您将取决于所使用的进程调度器的粒度。10毫秒可能是可以实现的,但请记住,在睡眠时,操作系统不会在可被唤醒时立即安排10毫秒。如果前面有其他进程,则可能会选择运行它们,因此您可能会被延迟 你的方法是一个很好的(或和你能得到的一样好的)近似
如果您需要更好的调度,您可以考虑编译启用实时选项的Linux内核,这将为您提供更精细的调度粒度。您将受制于所使用的进程调度器的粒度。10毫秒可能是可以实现的,但请记住,在睡眠时,操作系统不会在可被唤醒时立即安排10毫秒。如果前面有其他进程,则可能会选择运行它们,因此您可能会被延迟 你的方法是一个很好的(或和你能得到的一样好的)近似
如果您需要更好的调度,您可以考虑编译启用实时选项的Linux内核,这将为您提供更精细的调度粒度。您的方法会随着时间累积错误-例如,如果睡眠时间延长1毫秒一次,那么您将永远无法赶上备份。结果是,在很长的一段时间内,循环运行的次数比每10毫秒运行一次要少 为了避免这种情况,提前调用time函数一次,然后根据它计算未来的截止日期。使用带有单调时钟的
clock\u gettime()
比使用gettimeofday()
更可取,因为后者是实时时钟,因此在管理员更改系统时间时会受到影响
例如:
#include <time.h>
#include <errno.h>
void mythread(void *ptr )
{
struct timespec deadline;
clock_gettime(CLOCK_MONOTONIC, &deadline);
while(1)
{
//do stuff
/* Add 10ms to previous deadline */
deadline.tv_nsec += 10000000;
deadline.tv_sec += deadline.tv_nsec / 1000000000;
deadline.tv_nsec %= 1000000000;
/* Sleep until new deadline */
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL) != 0)
if (errno != EINTR) return;
}
return;
}
#包括
#包括
无效读取(无效*ptr)
{
结构timespec截止日期;
时钟获取时间(时钟单调和截止时间);
而(1)
{
//做事
/*在前一个截止日期的基础上增加10毫秒*/
deadline.tv_nsec+=10000000;
deadline.tv_sec+=deadline.tv_nsec/100000000;
deadline.tv_nsec%=100000000;
/*睡到新的截止日期*/
while(clock_nanosleep(clock_monotic,TIMER_ABSTIME,and deadline,NULL)!=0)
如果(errno!=EINTR)返回;
}
返回;
}
(在2.17之前的glibc版本上,您需要链接到
-lrt
以使用POSIX时钟功能)。您的方法会随着时间累积错误-例如,如果睡眠时间延长1毫秒一次,那么您将永远无法赶上备份。结果是,在很长的一段时间内,循环运行的次数比每10毫秒运行一次要少
为了避免这种情况,提前调用time函数一次,然后根据它计算未来的截止日期。使用带有单调时钟的clock\u gettime()
比使用gettimeofday()
更可取,因为后者是实时时钟,因此在管理员更改系统时间时会受到影响
例如:
#include <time.h>
#include <errno.h>
void mythread(void *ptr )
{
struct timespec deadline;
clock_gettime(CLOCK_MONOTONIC, &deadline);
while(1)
{
//do stuff
/* Add 10ms to previous deadline */
deadline.tv_nsec += 10000000;
deadline.tv_sec += deadline.tv_nsec / 1000000000;
deadline.tv_nsec %= 1000000000;
/* Sleep until new deadline */
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL) != 0)
if (errno != EINTR) return;
}
return;
}
#包括
#包括
无效读取(无效*ptr)
{
结构timespec截止日期;
时钟获取时间(时钟单调和截止时间);
而(1)
{
//做事
/*在前一个截止日期的基础上增加10毫秒*/
deadline.tv_nsec+=10000000;
deadline.tv_sec+=deadline.tv_nsec/100000000;
deadline.tv_nsec%=100000000;
/*睡到新的截止日期*/
while(clock_nanosleep(clock_monotic,TIMER_ABSTIME,and deadline,NULL)!=0)
如果(errno!=EINTR)返回;
}
返回;
}
(在2.17之前的glibc版本上,您需要链接到-lrt
以使用POSIX时钟函数)。我会使用实时信号(SIGRTMIN+0
到SIGRTMAX
),由基于clock\u单调
时钟的计时器和发布全局信号量的信号处理器触发
sem\u post()
是异步信号安全的,可以在信号处理程序中可靠地使用。这符合POSIX.1-2008,并且可以使POSIX.1-1990兼容;因此,这应该可以在所有操作系统上正常工作(Windows除外,通常情况下)
定时函数
/* This is POSIX C. strsignal() is in 200809L, otherwise 199309L is okay. */
#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <semaphore.h>
#include <string.h>
#include <stdio.h>
static volatile sig_atomic_t interrupted = 0;
/* Interrupt handler. Just updates the above variable to match the signal number.
*/
static void interrupt_handler(int signum)
{
interrupted = signum;
}
/* Install interrupt handler.
*/
static int interrupt_on(const int signum)
{
struct sigaction act;
if (signum < 1 || signum > SIGRTMAX)
return errno = EINVAL;
sigemptyset(&act.sa_mask);
act.sa_handler = interrupt_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
static timer_t periodic_timer;
static struct itimerspec periodic_interval;
static int periodic_signal = -1; /* Not installed */
static sem_t periodic_tick;
/* Periodic tick handler. Just posts the semaphore.
* Note: sem_post() is async-signal-safe.
*/
static void periodic_signal_handler(int signum)
{
if (signum == periodic_signal)
sem_post(&periodic_tick);
}
/* Install periodic tick. Returns 0 if success, errno error code otherwise.
*/
static int periodic_start(const int signum, const double interval_seconds)
{
struct sigaction act;
struct sigevent event;
/* Invalid signal number? Invalid interval? */
if (signum < 1 || signum > SIGRTMAX || interval_seconds <= 0.0)
return errno = EINVAL;
/* Verify there is no periodic signal yet. */
if (periodic_signal != -1)
return errno = EINVAL;
/* Initialize the semaphore. */
if (sem_init(&periodic_tick, 0, 0))
return errno;
/* Define interval. */
{
long s = (long)interval_seconds;
long ns = (long)(1000000000.0 * (interval_seconds - (double)s));
/* Overflow in seconds? */
if (s < 0L)
return errno = EINVAL;
/* Make sure ns is within limits. */
if (ns < 0L)
ns = 0L;
else if (ns > 999999999L)
ns = 999999999L;
/* Zero seconds maps to one nanosecond. */
if (s == 0L && ns == 0L)
ns = 1L;
periodic_interval.it_interval.tv_sec = (time_t)s;
periodic_interval.it_interval.tv_nsec = ns;
periodic_interval.it_value = periodic_interval.it_interval;
}
/* Install signal handler. */
sigemptyset(&act.sa_mask);
act.sa_handler = periodic_signal_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
/* Describe the periodic event: it is a signal. */
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = signum;
event.sigev_value.sival_ptr = NULL;
if (timer_create(CLOCK_MONOTONIC, &event, &periodic_timer) == -1) {
const int saved_errno = errno;
/* Uninstall the signal handler. */
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
sigaction(signum, &act, NULL);
/* Failed. */
return errno = saved_errno;
}
/* Arm the timer. */
if (timer_settime(periodic_timer, 0, &periodic_interval, NULL) == -1) {
const int saved_errno = errno;
/* Destroy the timer. */
timer_delete(periodic_timer);
/* Uninstall the signal handler. */
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
sigaction(signum, &act, NULL);
/* Failed. */
return errno = saved_errno;
}
/* Clear the overrun count. */
timer_getoverrun(periodic_timer);
/* Done. */
periodic_signal = signum;
return 0;
}
/* Uninstall periodic tick. Returns 0 if success, errno error code otherwise.
*/
static int periodic_stop(void)
{
sigset_t set, oldset;
struct sigaction action;
const struct timespec zerotimeout = { 0L, 0L };
const int signum = periodic_signal;
/* Not installed? */
if (signum == -1)
return 0;
/* Mark signal uninstalled. */
periodic_signal = -1;
/* Cancel the timer. This also disarms its interrupt. */
timer_delete(periodic_timer);
/* Create a signal set containing only the periodic signal. */
if (sigemptyset(&set) || sigaddset(&set, signum))
return errno;
/* Block the periodic signal. */
if (sigprocmask(SIG_BLOCK, &set, &oldset))
return errno;
/* Uninstall the signal handler. */
sigemptyset(&action.sa_mask);
action.sa_handler = SIG_DFL;
action.sa_flags = 0;
if (sigaction(signum, &action, NULL)) {
const int saved_errno = errno;
sigprocmask(SIG_SETMASK, &oldset, NULL);
return errno = saved_errno;
}
/* Dequeue all periodic signal interrupts. */
while (sigtimedwait(&set, NULL, &zerotimeout) == signum) {
/* Intentionally empty */
}
/* Restore the signal mask. */
if (sigprocmask(SIG_SETMASK, &oldset, NULL))
return errno;
/* Success. */
return 0;
}
int main(int argc, char *argv[])
{
double interval, output, duration, minduration, maxduration;
unsigned long limit, count = 0UL, skipped;
struct timespec prev, curr;
char dummy;
if (interrupt_on(SIGINT) || interrupt_on(SIGHUP) || interrupt_on(SIGTERM)) {
fprintf(stderr, "Cannot set interrupt handlers: %s.\n", strerror(errno));
return 1;
}
if (argc < 2 || argc > 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s interval [ count ]\n", argv[0]);
fprintf(stderr, "This program tests the timer interrupt jitter using semaphore wakeups.\n");
fprintf(stderr, "Interval is in seconds. The program will exit after count intervals.\n");
fprintf(stderr, "You can also interrupt the program with an INT (Ctrl-C), HUP, or TERM signal.\n");
fprintf(stderr, "\n");
return 0;
}
if (sscanf(argv[1], " %lf %c", &interval, &dummy) != 1) {
fprintf(stderr, "%s: Invalid interval in seconds.\n", argv[1]);
return 1;
} else
if (interval <= 0.0) {
fprintf(stderr, "%s: Interval must be positive!\n", argv[1]);
return 1;
}
if (argc > 2) {
if (sscanf(argv[2], " %lu %c", &limit, &dummy) != 1) {
fprintf(stderr, "%s: Invalid number of interrupts.\n", argv[2]);
return 1;
}
} else
limit = ~0UL;
if (periodic_start(SIGRTMIN+0, interval)) {
fprintf(stderr, "Cannot set up a periodic interrupt: %s.\n", strerror(errno));
return 1;
}
clock_gettime(CLOCK_REALTIME, &curr);
minduration = maxduration = interval;
output = 0.0;
skipped = 0UL;
printf("Interval is %lu.%09ld seconds.\n",
(unsigned long)periodic_interval.it_interval.tv_sec, periodic_interval.it_interval.tv_nsec);
fflush(stdout);
while (count++ < limit && !interrupted) {
while (!sem_trywait(&periodic_tick))
skipped++;
/* Wait for next tick. */
prev = curr;
while (sem_wait(&periodic_tick) == -1 && errno == EINTR);
clock_gettime(CLOCK_REALTIME, &curr);
duration = difftime(curr.tv_sec, prev.tv_sec) + ((double)curr.tv_nsec - (double)prev.tv_nsec) / 1000000000.0;
if (duration < minduration) minduration = duration;
if (duration > maxduration) maxduration = duration;
output += duration;
if (output >= 5.0) {
printf("Jitter: %+9.06f .. %+9.06f milliseconds, skipped %lu ticks\n",
(minduration - interval) * 1000.0,
(maxduration - interval) * 1000.0,
skipped);
fflush(stdout);
minduration = maxduration = duration;
output = 0.0;
skipped = 0UL;
}
}
if (output > 0.0)
printf("Jitter: %+9.06f .. %+9.06f milliseconds, skipped %lu ticks\n",
(minduration - interval) * 1000.0,
(maxduration - interval) * 1000.0,
skipped);
fflush(stdout);
periodic_stop();
if (interrupted)
fprintf(stderr, "%s.\n", strsignal(interrupted));
else
fprintf(stderr, "Completed.\n");
return 0;
}
gcc -W -Wall -O3 jitter.c -lrt -o jitter
./jitter 0.010