每隔1毫秒如何发送UDP数据包?

7

我需要编写一个应用程序在Linux上定期发送UDP包。理想情况下,频率应该是每1毫秒一次,并且数据包之间的时间间隔应保持一致。

我尝试过使用普通套接字来实现:

while(counter < 4294967295)
{
    for (k=0; k<4; k++) //Convert counter value to string
    {
        buf[k]=((unsigned char*)(&counter))[k];
    }
    sn = sendto(sender, &buf, sizeof(buf), 0, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); // Sending UDP segment
    if (sn < 0 ) error("UDP send fail!"); //Error handle
    counter++;

    nanosleep(&delay, NULL); //Sleep
}

在上述应用程序中,我只是用计数器值填充UDP数据包,以便在接收端区分它们。
基本上这段代码完成了它的工作,但存在以下问题: 1. 频率不够高,且容易受到主机性能和其他应用程序的影响。 2. 数据包间隔不一致,因为使用了RTC作为参考。然而,如果我尝试添加RTC检查,这会使数据包速率变得更慢。
我认为应该有更优雅的方法来实现我的目标,请您指点。

请注意,即使您设法让Linux框架每毫秒发送一个数据包,也不能保证(甚至不期望)接收方每毫秒接收到一个数据包 - 以太网交换机可以在数据包传播中引入延迟,并且通常会这样做。如果您确实需要精确的定时,请查看AVB和/或时间敏感型网络,其中交换机和端点已经针对一致的传递时间进行了工程设计:https://en.wikipedia.org/wiki/Audio_Video_Bridging https://en.wikipedia.org/wiki/Time-Sensitive_Networking - Jeremy Friesner
5个回答

5

在Linux(或任何UNIX系统)上实现重复的定时心跳,通常使用setitimer(2)函数:

#include <sys/time.h>
#include <signal.h>

struct itimerval timer;
timer.it_interval.tv_sec = timer.it_value.tv_sec = 0;
timer.it_interval.tv_usec = timer.it_value.tv_usec = 1000;   /* 1000 microseconds */
if (setitimer(ITIMER_REAL, &timer, 0) < 0) {
    perror("setitimer");
    exit(1); }

现在你每毫秒都会收到一个SIGALRM信号,因此你需要使用sigwait调用运行循环:

sigset_t alarm_sig;
int signum;
sigemptyset(&alarm_sig);
sigaddset(&alarm_sig, SIGALRM);
while (1) {
    sigwait(&alarm_sig, &signum); /* wait until the next signal */
    ... do your stuff to send a packet, or whatever.
}

现在,只要系统可以跟得上,您将每毫秒发送一个数据包。后者非常重要--如果系统负载过重(或创建数据包的代码太慢),下一个信号会在下一个sigwait调用之前到达并终止您的进程。如果您不想发生这种情况,请为SIGALARM信号添加信号处理程序:

void missed_alarm(int signum) {
    /* we missed a timer signal, so won't be sending packets fast enough!!! */
    write(2, "Missed Alarm!\n", 14); /* can't use printf in a signal handler */
}

signal(SIGALRM, missed_alarm);

现在,如果一个警报被错过了,你的数据包发送将会减慢(你会错过一个时间段),但你会在stderr上收到相关信息。
上述方法存在一个重大问题,即它依赖于系统计时器的分辨率。在Linux系统上,这严重依赖于内核配置CONFIG_HIGH_RES_TIMERS。如果未启用该选项,则你可能只有10毫秒的分辨率,因此尝试使用1毫秒时钟将会失败。我相信它已经默认启用于大多数x86_64发行版中,但你可以通过查看/proc/timer_list并查看“ktime_get_real”时钟的分辨率来进行检查。

这个解决方案完全符合我的需求。 我已经在Debian 7 ARM下的1 Ghz ARM平台上进行了测试,结果非常一致。 使用1毫秒定时器,我没有错过任何警报,并且WireShark显示数据包之间的间隔接近于1毫秒。 非常感谢您的帮助! - Xia

2
如果你想保持一致性,你应该将流程调整到一个核心,并使用_mm_pause()在忙碌的循环中"烧掉"该核心。然后,在每个循环迭代中检查时钟,如果时间已经过去了一定的时间,就会发送一个数据包。
如果你想要非常一致,你应该从开始计时,并在每次发送数据包时增加“下一个超时”变量。但要小心:当计算机进入睡眠状态或其他情况时,很容易失控,然后唤醒并认为是时间发送100000个数据包(只需问问Adobe,在大约十年前的Flash中就有这个漏洞)。
哦,显然不要在像笔记本电脑或手机这样的功率受限设备上执行此操作。

2

您可以实现一个内核模式组件来发送UDP数据包,从而避免用户模式和内核模式之间的上下文切换。同时,您可以访问高精度计时器以几乎实时地发送数据包。


这个基本上是一个自定义的网络卡驱动程序,它是唯一能够工作的东西(至少在你可以控制它的范围内——如果物理线路忙碌,你仍然无法按时发送)。否则,即使你准确地按时发送数据包,你也无法知道它何时会被发送出去。 - Damon

1
使用2个线程。 主线程。
pthread_mutex_lock(&sendLock);
/* create worker thread to send packet */
while (counter < 4294967295) {
    pthread_mutex_unlock(&sendLock);
    /* nano sleep for very small interval */
    sched_yield();
    pthread_mutex_lock(&sendLock);

    /* nano sleep for remaining time interval */
}

同时,在工作线程中。
while (1) {
    pthread_mutex_lock(&sendLock);
    pthread_mutex_unlock(&sendLock);
    /* udp send code and counter increment*/ 
    for (k=0; k<4; k++) //Convert counter value to string
    {
        buf[k]=((unsigned char*)(&counter))[k];
    }
    sn = sendto(sender, &buf, sizeof(buf), 0, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); // Sending UDP segment
    if (sn < 0 ) error("UDP send fail!"); //Error handle
    counter++;
    sched_yield();
}

这将在一定程度上确保每个时间间隔只发送1个数据包


1
您不能使用互斥锁来以这种方式保护计时器间隔,因为无法保证发送循环不会连续执行多次。 - nos
nano睡眠本来可以完成工作,我接受了您的意见并添加了显式的sched_yield()。 - Nish

0

如何在Linux中轻松运行高分辨率、高精度的周期循环,任意频率(例如:最高可达10 KHz〜100 KHz),使用软实时调度程序和纳秒延迟

这样做相当复杂。但是,借助我编写的一些辅助函数,这非常容易。因此,让我首先向您展示简单部分。

以下是如何在Linux中设置以固定频率10 KHz运行的循环(根据您的代码和系统,可能可以实现高达10 KHz至100 KHz的频率)。在此处获取timinglib.h

// My timing library, here: 
// https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/c/timinglib.h
#include "timinglib.h"

// A task to start as a new pthread posix thread
void * pthread_task(void * argument)
{
    // Activate the `SCHED_RR` Linux soft real-time round-robin scheduler, and
    // turn on memory locking to lock your process's RAM so it can't be moved
    // to swap space by the kernel, since that would mess up your program's
    // timing.
    use_realtime_scheduler();

    // SET LOOP PERIOD (FREQUENCY) HERE!
    //    10 us ( 0.01 ms) --> 100 KHz
    //   100 us ( 0.10 ms) -->  10 KHz
    //  1000 us ( 1.00 ms) -->   1 KHz
    // 10000 us (10.00 ms) --> 100 Hz
    const uint64_t PERIOD_US = 100; // 10 KHz loop frequency

    // Seed the last wake time with the current time
    uint64_t last_wake_time_us = micros();

    while (true)
    {
        // Wait for the next cycle.
        sleep_until_us(&last_wake_time_us, PERIOD_US);

        // ---------------------------------------------------------------------
        // Perform whatever action you want here at this fixed interval.
        // ---------------------------------------------------------------------
    }
}

就是这样!非常简单。把你的套接字代码放在那里以发送UDP数据包。

我在这里有一个UDP套接字演示:

  1. 客户端
  2. 服务器

现在,让我们谈谈上面的定时问题。

sleep_until_us() 的工作方式就像 FreeRTOS函数vTaskDelayUntil() 一样。我喜欢它使用起来非常容易,所以我让我的函数在Linux中也像它一样工作。

为了做到这一点,我必须使用带有标志 TIMER_ABSTIMEclock_nanosleep() 来睡眠,以等待未来(您期望的周期)设置的绝对时间,而不是从现在开始计算的相对时间。

在这里阅读相关内容:https://man7.org/linux/man-pages/man2/clock_nanosleep.2.html

查看我在timinglib.c中实现该函数的调用:

void sleep_until_us(uint64_t * previous_wake_time_us, uint64_t period_us)
{
    if (previous_wake_time_us == NULL)
    {
        printf("ERROR: NULL ptr.\n");
        return;
    }

    uint64_t previous_wake_time_ns = US_TO_NS(*previous_wake_time_us);
    uint64_t period_ns = US_TO_NS(period_us);

    sleep_until_ns(&previous_wake_time_ns, period_ns);
    *previous_wake_time_us = NS_TO_US(previous_wake_time_ns);
}

void sleep_until_ns(uint64_t * previous_wake_time_ns, uint64_t period_ns)
{
    // See "sleep_nanosleep.c" and "sleep_nanosleep_minimum_time_interval.c" for sleep examples.

    if (previous_wake_time_ns == NULL)
    {
        printf("ERROR: NULL ptr.\n");
        return;
    }

    // Generate an absolute timestamp at a future point in time, at which point we want to
    // wake up after sleeping.
    uint64_t time_wakeup_ns = *previous_wake_time_ns + period_ns;
    *previous_wake_time_ns = time_wakeup_ns; // update the user's input variable
    const struct timespec TS_WAKEUP =
    {
        .tv_sec = (__time_t)(time_wakeup_ns / NS_PER_SEC),
        .tv_nsec = (__syscall_slong_t)(time_wakeup_ns % NS_PER_SEC),
    };

    // If the sleep is interrupted, it may take a couple attempts to sleep the full
    // amount--hence the while loop.
    int retcode = EINTR; // force to run once
    while (retcode == EINTR)
    {
        retcode = clock_nanosleep(CLOCK_TYPE, TIMER_ABSTIME, &TS_WAKEUP, NULL);
        if (retcode != 0)
        {
            print_nanosleep_failed(retcode);
        }
    }
}

但这还不够。使用常规的Linux SCHED_OTHER调度程序,clock_nanosleep()的最小睡眠间隔约为55微秒,误差高达1毫秒。这对于1~10+ KHz循环来说不够好。因此,我们激活了SCHED_RR软实时轮询调度程序,以获得约4微秒的最小睡眠间隔,误差高达0.4毫秒。这样就好多了。

请查看我的答案:如何睡眠几微秒

这是我的调度程序和内存锁定配置代码,来自timinglib.c

// NB: for implementation details, see my examples inside the `set_scheduler()` func
// in "sleep_nanosleep_minimum_time_interval.c"
void use_realtime_scheduler()
{
    int retcode;

    pthread_t this_thread = pthread_self();
    const struct sched_param priority_param =
    {
        // the priority must be from 1 (lowest priority) to 99
        // (highest priority) for the `SCHED_FIFO` AND `SCHED_RR`
        // (round robin) scheduler policies; see:
        // https://man7.org/linux/man-pages/man7/sched.7.html
        .sched_priority = REALTIME_SCHEDULER_PRIORITY_LOWEST,
    };
    retcode = pthread_setschedparam(this_thread, SCHED_RR, &priority_param);
    if (retcode != 0)
    {
        printf("ERROR: in file %s: %i: Failed to set pthread scheduler. "
               "retcode = %i: %s.\n",
                __FILE__, __LINE__, retcode, strerror(retcode));
        if (retcode == EPERM)  // Error: Permissions
        {
            printf("  You must use `sudo` or run this program as root to "
                   "have proper privileges!\n");
        }
    }
    else
    {
        // printf("`pthread_setschedparam()` successful.\n");
    }

    // Memory lock: also lock the memory into RAM to prevent slow operations
    // where the kernel puts it into swap space. See notes above.
    retcode = mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
    if (retcode == -1)
    {
        printf("ERROR: in file %s: %i: Failed to lock memory into RAM. "
               "errno = %i: %s.\n",
            __FILE__, __LINE__, errno, strerror(errno));
        if (errno == EPERM)  // Error: Permissions
        {
            printf("  You must use `sudo` or run this program as root to "
                   "have proper privileges!\n");
        }
    }
    else
    {
        // printf("`mlockall()` successful.\n");
    }

}

这里有一个带有时间仪器的完整演示,用于测试您系统上可能达到的最高速度:timinglib_pthread_periodic_loop.c

当设置为 100 微秒 的循环周期(10 KHz)时,这是输出和误差。请注意误差的好坏!大多数循环迭代的误差都小于 1%,最坏情况下偶尔会出现 +/- 20% 的误差。对于 10 KHz,这非常好!

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c17 timinglib_pthread_periodic_loop.c timinglib.c -o bin/a -pthread && time sudo bin/a
Activating realtime scheduler.
Starting pthread at fixed interval using `sleep_until_us()`.
thread_name = some thread name
loop period = 100 us; freq = 10000.0 Hz
   0: dt_ns =     85717 ns;  error =  14283 **ns** ( 14.283%)
   1: dt_ns =     98792 ns;  error =   1208 **ns** (  1.208%)
   2: dt_ns =     99712 ns;  error =    288 **ns** (  0.288%)
   3: dt_ns =    100196 ns;  error =   -196 **ns** ( -0.196%)
   4: dt_ns =     99679 ns;  error =    321 **ns** (  0.321%)
   5: dt_ns =    100680 ns;  error =   -680 **ns** ( -0.680%)
   6: dt_ns =     99396 ns;  error =    604 **ns** (  0.604%)
   7: dt_ns =    100265 ns;  error =   -265 **ns** ( -0.265%)
   8: dt_ns =     99716 ns;  error =    284 **ns** (  0.284%)
   9: dt_ns =    100183 ns;  error =   -183 **ns** ( -0.183%)
  10: dt_ns =     99864 ns;  error =    136 **ns** (  0.136%)
  11: dt_ns =    100031 ns;  error =    -31 **ns** ( -0.031%)
  12: dt_ns =    100001 ns;  error =     -1 **ns** ( -0.001%)
  13: dt_ns =     99970 ns;  error =     30 **ns** (  0.030%)
  14: dt_ns =     99984 ns;  error =     16 **ns** (  0.016%)
  15: dt_ns =    100047 ns;  error =    -47 **ns** ( -0.047%)
  16: dt_ns =     99861 ns;  error =    139 **ns** (  0.139%)
  17: dt_ns =    100281 ns;  error =   -281 **ns** ( -0.281%)
  18: dt_ns =     99909 ns;  error =     91 **ns** (  0.091%)
  19: dt_ns =     99985 ns;  error =     15 **ns** (  0.015%)
  20: dt_ns =     99736 ns;  error =    264 **ns** (  0.264%)
  21: dt_ns =    100312 ns;  error =   -312 **ns** ( -0.312%)
  22: dt_ns =    100013 ns;  error =    -13 **ns** ( -0.013%)
  23: dt_ns =    100014 ns;  error =    -14 **ns** ( -0.014%)
  24: dt_ns =     99834 ns;  error =    166 **ns** (  0.166%)
  25: dt_ns =     99951 ns;  error =     49 **ns** (  0.049%)
  26: dt_ns =    100267 ns;  error =   -267 **ns** ( -0.267%)
  27: dt_ns =     99735 ns;  error =    265 **ns** (  0.265%)
  28: dt_ns =    100174 ns;  error =   -174 **ns** ( -0.174%)
  29: dt_ns =    100028 ns;  error =    -28 **ns** ( -0.028%)
  30: dt_ns =     99942 ns;  error =     58 **ns** (  0.058%)
  31: dt_ns =     99747 ns;  error =    253 **ns** (  0.253%)
  32: dt_ns =    100226 ns;  error =   -226 **ns** ( -0.226%)
  33: dt_ns =     99994 ns;  error =      6 **ns** (  0.006%)
  34: dt_ns =     99969 ns;  error =     31 **ns** (  0.031%)
  35: dt_ns =     99857 ns;  error =    143 **ns** (  0.143%)
  36: dt_ns =    100386 ns;  error =   -386 **ns** ( -0.386%)
  37: dt_ns =     99813 ns;  error =    187 **ns** (  0.187%)
  38: dt_ns =    100042 ns;  error =    -42 **ns** ( -0.042%)
  39: dt_ns =     99706 ns;  error =    294 **ns** (  0.294%)
  40: dt_ns =    100238 ns;  error =   -238 **ns** ( -0.238%)
  41: dt_ns =     99886 ns;  error =    114 **ns** (  0.114%)
  42: dt_ns =    100160 ns;  error =   -160 **ns** ( -0.160%)
  43: dt_ns =     99867 ns;  error =    133 **ns** (  0.133%)
  44: dt_ns =    100092 ns;  error =    -92 **ns** ( -0.092%)
  45: dt_ns =     99878 ns;  error =    122 **ns** (  0.122%)
  46: dt_ns =    100085 ns;  error =    -85 **ns** ( -0.085%)
  47: dt_ns =    100112 ns;  error =   -112 **ns** ( -0.112%)
  48: dt_ns =     99764 ns;  error =    236 **ns** (  0.236%)
  49: dt_ns =    100212 ns;  error =   -212 **ns** ( -0.212%)
  50: dt_ns =     99989 ns;  error =     11 **ns** (  0.011%)
  51: dt_ns =    100010 ns;  error =    -10 **ns** ( -0.010%)
  52: dt_ns =     99759 ns;  error =    241 **ns** (  0.241%)
  53: dt_ns =    100206 ns;  error =   -206 **ns** ( -0.206%)
  54: dt_ns =    100002 ns;  error =     -2 **ns** ( -0.002%)
  55: dt_ns =     99938 ns;  error =     62 **ns** (  0.062%)
  56: dt_ns =     99746 ns;  error =    254 **ns** (  0.254%)
  57: dt_ns =    100569 ns;  error =   -569 **ns** ( -0.569%)
  58: dt_ns =     99724 ns;  error =    276 **ns** (  0.276%)
  59: dt_ns =    100101 ns;  error =   -101 **ns** ( -0.101%)
  60: dt_ns =     99650 ns;  error =    350 **ns** (  0.350%)
  61: dt_ns =    100250 ns;  error =   -250 **ns** ( -0.250%)
  62: dt_ns =    100001 ns;  error =     -1 **ns** ( -0.001%)
  63: dt_ns =    100052 ns;  error =    -52 **ns** ( -0.052%)
  64: dt_ns =     99853 ns;  error =    147 **ns** (  0.147%)
  65: dt_ns =     99832 ns;  error =    168 **ns** (  0.168%)
  66: dt_ns =    100017 ns;  error =    -17 **ns** ( -0.017%)
  67: dt_ns =    100001 ns;  error =     -1 **ns** ( -0.001%)
  68: dt_ns =    100227 ns;  error =   -227 **ns** ( -0.227%)
  69: dt_ns =     99840 ns;  error =    160 **ns** (  0.160%)
  70: dt_ns =     99876 ns;  error =    124 **ns** (  0.124%)
  71: dt_ns =     99938 ns;  error =     62 **ns** (  0.062%)
  72: dt_ns =    100469 ns;  error =   -469 **ns** ( -0.469%)
  73: dt_ns =    100162 ns;  error =   -162 **ns** ( -0.162%)
  74: dt_ns =    100724 ns;  error =   -724 **ns** ( -0.724%)
  75: dt_ns =    106371 ns;  error =  -6371 **ns** ( -6.371%)
  76: dt_ns =     93393 ns;  error =   6607 **ns** (  6.607%)
  77: dt_ns =    100476 ns;  error =   -476 **ns** ( -0.476%)
  78: dt_ns =     99400 ns;  error =    600 **ns** (  0.600%)
  79: dt_ns =     99948 ns;  error =     52 **ns** (  0.052%)
  80: dt_ns =     99938 ns;  error =     62 **ns** (  0.062%)
  81: dt_ns =    100204 ns;  error =   -204 **ns** ( -0.204%)
  82: dt_ns =    100026 ns;  error =    -26 **ns** ( -0.026%)
  83: dt_ns =    100236 ns;  error =   -236 **ns** ( -0.236%)
  84: dt_ns =     99252 ns;  error =    748 **ns** (  0.748%)
  85: dt_ns =    100272 ns;  error =   -272 **ns** ( -0.272%)
  86: dt_ns =     99745 ns;  error =    255 **ns** (  0.255%)
  87: dt_ns =    101421 ns;  error =  -1421 **ns** ( -1.421%)
  88: dt_ns =     99283 ns;  error =    717 **ns** (  0.717%)
  89: dt_ns =    100878 ns;  error =   -878 **ns** ( -0.878%)
  90: dt_ns =     99288 ns;  error =    712 **ns** (  0.712%)
  91: dt_ns =     99430 ns;  error =    570 **ns** (  0.570%)
  92: dt_ns =     99673 ns;  error =    327 **ns** (  0.327%)
  93: dt_ns =    100080 ns;  error =    -80 **ns** ( -0.080%)
  94: dt_ns =     99945 ns;  error =     55 **ns** (  0.055%)
  95: dt_ns =     99950 ns;  error =     50 **ns** (  0.050%)
  96: dt_ns =     99985 ns;  error =     15 **ns** (  0.015%)
  97: dt_ns =    100418 ns;  error =   -418 **ns** ( -0.418%)
  98: dt_ns =    100050 ns;  error =    -50 **ns** ( -0.050%)
  99: dt_ns =     99361 ns;  error =    639 **ns** (  0.639%)
 100: dt_ns =     99627 ns;  error =    373 **ns** (  0.373%)
 101: dt_ns =     99641 ns;  error =    359 **ns** (  0.359%)
 102: dt_ns =    100025 ns;  error =    -25 **ns** ( -0.025%)
 103: dt_ns =    100820 ns;  error =   -820 **ns** ( -0.820%)
 104: dt_ns =    100723 ns;  error =   -723 **ns** ( -0.723%)
 105: dt_ns =     98815 ns;  error =   1185 **ns** (  1.185%)
 106: dt_ns =    100250 ns;  error =   -250 **ns** ( -0.250%)
 107: dt_ns =    100216 ns;  error =   -216 **ns** ( -0.216%)
 108: dt_ns =     99683 ns;  error =    317 **ns** (  0.317%)
 109: dt_ns =    100966 ns;  error =   -966 **ns** ( -0.966%)
 110: dt_ns =    100357 ns;  error =   -357 **ns** ( -0.357%)
 111: dt_ns =    100022 ns;  error =    -22 **ns** ( -0.022%)
 112: dt_ns =     98966 ns;  error =   1034 **ns** (  1.034%)
 113: dt_ns =     99517 ns;  error =    483 **ns** (  0.483%)
 114: dt_ns =     99973 ns;  error =     27 **ns** (  0.027%)
 115: dt_ns =     99841 ns;  error =    159 **ns** (  0.159%)
 116: dt_ns =    101627 ns;  error =  -1627 **ns** ( -1.627%)
 117: dt_ns =    100344 ns;  error =   -344 **ns** ( -0.344%)
 118: dt_ns =     99767 ns;  error =    233 **ns** (  0.233%)
 119: dt_ns =    100106 ns;  error =   -106 **ns** ( -0.106%)
 120: dt_ns =    101530 ns;  error =  -1530 **ns** ( -1.530%)
 121: dt_ns =     99844 ns;  error =    156 **ns** (  0.156%)
 122: dt_ns =     98751 ns;  error =   1249 **ns** (  1.249%)
 123: dt_ns =    100082 ns;  error =    -82 **ns** ( -0.082%)
 124: dt_ns =     99979 ns;  error =     21 **ns** (  0.021%)
 125: dt_ns =    101888 ns;  error =  -1888 **ns** ( -1.888%)
 126: dt_ns =     99798 ns;  error =    202 **ns** (  0.202%)
 127: dt_ns =     98897 ns;  error =   1103 **ns** (  1.103%)
 128: dt_ns =    100091 ns;  error =    -91 **ns** ( -0.091%)
 129: dt_ns =     99992 ns;  error =      8 **ns** (  0.008%)
 130: dt_ns =    100077 ns;  error =    -77 **ns** ( -0.077%)
 131: dt_ns =     99306 ns;  error =    694 **ns** (  0.694%)
 132: dt_ns =    100008 ns;  error =     -8 **ns** ( -0.008%)
 133: dt_ns =    100690 ns;  error =   -690 **ns** ( -0.690%)
 134: dt_ns =    100179 ns;  error =   -179 **ns** ( -0.179%)
 135: dt_ns =     97880 ns;  error =   2120 **ns** (  2.120%)
 136: dt_ns =     99795 ns;  error =    205 **ns** (  0.205%)
 137: dt_ns =    100787 ns;  error =   -787 **ns** ( -0.787%)
 138: dt_ns =    102552 ns;  error =  -2552 **ns** ( -2.552%)
 139: dt_ns =     99397 ns;  error =    603 **ns** (  0.603%)
 140: dt_ns =     99718 ns;  error =    282 **ns** (  0.282%)
 141: dt_ns =     99864 ns;  error =    136 **ns** (  0.136%)
 142: dt_ns =    101029 ns;  error =  -1029 **ns** ( -1.029%)
 143: dt_ns =    104776 ns;  error =  -4776 **ns** ( -4.776%)
 144: dt_ns =     94933 ns;  error =   5067 **ns** (  5.067%)
 145: dt_ns =     99679 ns;  error =    321 **ns** (  0.321%)
 146: dt_ns =     99559 ns;  error =    441 **ns** (  0.441%)
 147: dt_ns =    100669 ns;  error =   -669 **ns** ( -0.669%)
 148: dt_ns =    100517 ns;  error =   -517 **ns** ( -0.517%)
 149: dt_ns =     98934 ns;  error =   1066 **ns** (  1.066%)
 150: dt_ns =     98797 ns;  error =   1203 **ns** (  1.203%)
 151: dt_ns =     99370 ns;  error =    630 **ns** (  0.630%)
 152: dt_ns =     99447 ns;  error =    553 **ns** (  0.553%)
 153: dt_ns =     99903 ns;  error =     97 **ns** (  0.097%)
 154: dt_ns =    101088 ns;  error =  -1088 **ns** ( -1.088%)
 155: dt_ns =     99971 ns;  error =     29 **ns** (  0.029%)
 156: dt_ns =     99980 ns;  error =     20 **ns** (  0.020%)
 157: dt_ns =     99390 ns;  error =    610 **ns** (  0.610%)
 158: dt_ns =    102007 ns;  error =  -2007 **ns** ( -2.007%)
 159: dt_ns =     99097 ns;  error =    903 **ns** (  0.903%)
 160: dt_ns =     98546 ns;  error =   1454 **ns** (  1.454%)
 161: dt_ns =     99841 ns;  error =    159 **ns** (  0.159%)
 162: dt_ns =    100830 ns;  error =   -830 **ns** ( -0.830%)
 163: dt_ns =    100135 ns;  error =   -135 **ns** ( -0.135%)
 164: dt_ns =    101267 ns;  error =  -1267 **ns** ( -1.267%)
 165: dt_ns =    103445 ns;  error =  -3445 **ns** ( -3.445%)
 166: dt_ns =     99046 ns;  error =    954 **ns** (  0.954%)
 167: dt_ns =     99528 ns;  error =    472 **ns** (  0.472%)
 168: dt_ns =    100012 ns;  error =    -12 **ns** ( -0.012%)
 169: dt_ns =    100580 ns;  error =   -580 **ns** ( -0.580%)
 170: dt_ns =     97971 ns;  error =   2029 **ns** (  2.029%)
 171: dt_ns =     99363 ns;  error =    637 **ns** (  0.637%)
 172: dt_ns =    100817 ns;  error =   -817 **ns** ( -0.817%)
 173: dt_ns =    101567 ns;  error =  -1567 **ns** ( -1.567%)
 174: dt_ns =    100112 ns;  error =   -112 **ns** ( -0.112%)
 175: dt_ns =     99775 ns;  error =    225 **ns** (  0.225%)
 176: dt_ns =    100885 ns;  error =   -885 **ns** ( -0.885%)
 177: dt_ns =     99555 ns;  error =    445 **ns** (  0.445%)
 178: dt_ns =    101252 ns;  error =  -1252 **ns** ( -1.252%)
 179: dt_ns =     99116 ns;  error =    884 **ns** (  0.884%)
 180: dt_ns =     99471 ns;  error =    529 **ns** (  0.529%)
 181: dt_ns =     98410 ns;  error =   1590 **ns** (  1.590%)
 182: dt_ns =    100764 ns;  error =   -764 **ns** ( -0.764%)
 183: dt_ns =     99709 ns;  error =    291 **ns** (  0.291%)
 184: dt_ns =     99505 ns;  error =    495 **ns** (  0.495%)
 185: dt_ns =    101294 ns;  error =  -1294 **ns** ( -1.294%)
 186: dt_ns =     98697 ns;  error =   1303 **ns** (  1.303%)
 187: dt_ns =    101129 ns;  error =  -1129 **ns** ( -1.129%)
 188: dt_ns =     99346 ns;  error =    654 **ns** (  0.654%)
 189: dt_ns =    100789 ns;  error =   -789 **ns** ( -0.789%)
 190: dt_ns =     97991 ns;  error =   2009 **ns** (  2.009%)
 191: dt_ns =    101046 ns;  error =  -1046 **ns** ( -1.046%)
 192: dt_ns =     98505 ns;  error =   1495 **ns** (  1.495%)
 193: dt_ns =     99308 ns;  error =    692 **ns** (  0.692%)
 194: dt_ns =     99995 ns;  error =      5 **ns** (  0.005%)
 195: dt_ns =    100440 ns;  error =   -440 **ns** ( -0.440%)
 196: dt_ns =    100826 ns;  error =   -826 **ns** ( -0.826%)
 197: dt_ns =    102797 ns;  error =  -2797 **ns** ( -2.797%)
 198: dt_ns =     97970 ns;  error =   2030 **ns** (  2.030%)
 199: dt_ns =     98622 ns;  error =   1378 **ns** (  1.378%)
average time error per iteration =       69.690 ns (  0.070%)
minimum time error per iteration =    -6371     ns ( -6.371%)
maximum time error per iteration =    14283     ns ( 14.283%)
Final message from thread = Done!
real    0m0.049s
user    0m0.004s
sys 0m0.015s

你可以自己运行它。克隆我的仓库并运行代码。以下是从文件顶部开始的gcc构建和运行命令:

gcc -Wall -Wextra -Werror -O3 -std=c17 timinglib_pthread_periodic_loop.c \
timinglib.c -o bin/a -pthread && time sudo bin/a

另请参阅:

  1. [我的回答] 如何睡眠几微秒并配置 SCHED_RR - 如何配置Linux的SCHED_RR软实时轮询调度程序,以便clock_nanosleep()可以从~55 us提高分辨率到~4 us
  2. [我的回答] pthread_create与pthread_attr_setschedparam无法正常工作
  3. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接