简体   繁体   中英

How to send UDP packet every 1 ms?

I need to write an application for Linux that will periodically send UDP packet. Ideally the frequency should be every 1 ms and the interval between packets should be consistent.

I have tried to do it via usual sockets this way:

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
}

In the application above I just fill the UDP packet with a counter value, so I can distinguish them on the reciever's end.

Basically this code does it's job, but has these problems: 1. Frequency is NOT high enough and highly affected by host perfomance and other applications. 2. Packet interval is not consistent, because there is RTC used as a reference. However if I tried to put RTC check, that made packet rate even slower.

I assume ther should be more elegant way to acheive my goals with different approach. Please kindly advise.

The standard way of getting a repeating regular heartbeat sort of thing on linux (or any UNIX for that matter) is to use 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); }

Now you'll be getting a SIGALRM signal every millisecond, so you run your loop with a sigwait call:

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.
}

Now you'll be sending a packet every millisecond AS LONG AS THE SYSTEM CAN KEEP UP. The latter is important -- if the system is too heavily loaded (or your code to create a packet is too slow), the next signal will come in before the next sigwait call and kill your process. If you don't want that, put in a signal handler for SIGALARM signals:

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

Now if an alarm is missed, your packet sends will be slowed (you'll miss a slot), but you'll get a message about it on stderr.


One significant issue with the above is that it's dependent on your system timer resolution. On Linux, this depends heavily on the kernel configuration CONFIG_HIGH_RES_TIMERS. If that is not enabled, you likely only have 10ms resolution, so trying to use a 1ms clock will fail badly. I believe it is enabled by default on most x86_64 distributions, but you can check by looking in /proc/timer_list and see what the resolution of the 'ktime_get_real' clock is.

If you want it to be consistent you should affine your process to one core, and "burn" that core by spinning in a busy loop with _mm_pause() at the bottom. You'll then check the clock in each loop iteration, and if the right amount of time has passed, fire off a packet.

If you want to be quite consistent you should count time from the beginning and bump your "next timeout" variable each time you send a packet. But be careful: it would be easy to run amok when the computer is put to sleep or so, then wakes back up and thinks it is time to fire 100000 packets (just ask Adobe, they had this bug in Flash about a decade ago).

Oh, and obviously don't do this on a power-limited device like a laptop or phone.

You can implement a kernel mode component to send the UDP packet which can avoid the context switches between user & kernel mode. At the same time you can get access to high precision timer to send the packets almost real time.

use 2 threads. main thread

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 */
}

meanwhile in worker thread

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();
}

this will make somewhat sure that 1 packet is send per time interval

How to run a high-resolution, high-precision periodic loop in Linux easily, at any frequency (ex: up to 10 KHz~100 KHz) using a soft real-time scheduler and nanosecond delays

Doing this well is pretty complicated. But, with a few helper functions I wrote, it is really easy. So, let me show you the easy part first.

Here is how to set up a loop in Linux which runs at a fixed frequency of 10 KHz (frequencies up to 10 KHz to 100 KHz may be possible, depending on your code and system). Get timinglib.h here.

// 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.
        // ---------------------------------------------------------------------
    }
}

That's it. Super simple. Stick your socket code to send UDP packets in there.

I have a UDP socket demo here:

  1. client
  2. server

Now, let's talk about the timing stuff above.

sleep_until_us() works just like the FreeRTOS function vTaskDelayUntil() . I liked how easy that was to use, so I made my function act like it in Linux.

To do it, I must use clock_nanosleep() with the flag TIMER_ABSTIME to sleep until an absolute time you set in the future (at your desired period), rather than for a relative time from now.

Read about that here: https://man7.org/linux/man-pages/man2/clock_nanosleep.2.html

See my call to that function in my implementation in 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);
        }
    }
}

But, that's not enough. With the regular Linux SCHED_OTHER scheduler, clock_nanosleep() has a minimum sleep interval of about 55 us , with an error up to 1 ms . That's not good enough for 1~10+ KHz loops. So, we activate the SCHED_RR soft real-time round robin scheduler to get minimum sleep intervals of about 4 us , with an error up to 0.4 ms . That's much better.

Study my answer here: How to sleep for a few microseconds

Here is my scheduler and memory lock configuration code from 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");
    }

}

Here is a full demo with timing instrumentation to test the max speeds possible on your system: timinglib_pthread_periodic_loop.c .

When set to a 100 us loop period ( 10 KHz ), here is the output and error. Notice how good the error is, Most loop iterations are < 1% error. with the worst-case being +/- 20% error on occasion. For 10 KHz , that's great!

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

You can run it yourself. Clone my repo and run the code. Here is the gcc build and run command, from the top of the file:

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

See also:

  1. [my answer] How to sleep for a few microseconds and configure SCHED_RR - How to configure the Linux SCHED_RR soft real-time round-robin scheduler so that clock_nanosleep() can have improved resolution of ~4 us down from ~55 us
  2. [my answer] pthread_create not working properly with pthread_attr_setschedparam
  3. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM