¿Cómo enviar paquetes UDP cada 1 ms?

17 minutos de lectura

Necesito escribir una aplicación para Linux que envíe paquetes UDP periódicamente. Idealmente, la frecuencia debe ser cada 1 ms y el intervalo entre paquetes debe ser constante.

He intentado hacerlo a través de sockets habituales de esta manera:

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
}

En la aplicación anterior, solo llené el paquete UDP con un valor de contador, para poder distinguirlos en el extremo del receptor.

Básicamente, este código hace su trabajo, pero tiene estos problemas: 1. La frecuencia NO es lo suficientemente alta y se ve muy afectada por el rendimiento del host y otras aplicaciones. 2. El intervalo del paquete no es consistente, porque se usa RTC como referencia. Sin embargo, si traté de poner la verificación RTC, eso hizo que la tasa de paquetes fuera aún más lenta.

Supongo que debería haber una forma más elegante de lograr mis objetivos con un enfoque diferente. Por favor, amablemente asesorar.

  • Tenga en cuenta que incluso si logra que su caja de Linux envíe exactamente un paquete por milisegundo, no hay garantía (o incluso expectativa) de que el receptor reciba exactamente un paquete por milisegundo: los conmutadores Ethernet pueden introducir retrasos en la propagación de paquetes, y a menudo lo hacen. Si realmente necesita una sincronización exacta, puede buscar AVB y/o redes sensibles al tiempo, en las que el conmutador y los puntos finales se han diseñado para una sincronización de entrega consistente: en.wikipedia.org/wiki/Audio_Video_Puente en.wikipedia.org/wiki/Time-Sensitive_Networking

    –Jeremy Friesner

    8 abr a las 18:11

avatar de usuario
chris dodd

La forma estándar de obtener un latido del corazón regular repetitivo en Linux (o cualquier UNIX para el caso) es usar 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); }

Ahora recibirá una señal SIGALRM cada milisegundo, por lo que ejecuta su ciclo con un sigwait llamar:

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

Ahora estará enviando un paquete cada milisegundo MIENTRAS EL SISTEMA PUEDE SEGUIR EL RITMO. Esto último es importante: si el sistema está demasiado cargado (o su código para crear un paquete es demasiado lento), la siguiente señal llegará antes que la siguiente. sigwait llame y elimine su proceso. Si no quiere eso, coloque un controlador de señal para señales 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);

Ahora, si se pierde una alarma, los envíos de paquetes se ralentizarán (perderá una ranura), pero recibirá un mensaje al respecto en stderr.


Un problema importante con lo anterior es que depende de la resolución del temporizador de su sistema. En Linux, esto depende en gran medida de la configuración del núcleo CONFIG_HIGH_RES_TIMERS. Si eso no está habilitado, es probable que solo tenga una resolución de 10 ms, por lo que intentar usar un reloj de 1 ms fallará gravemente. Creo que está habilitado de forma predeterminada en la mayoría de las distribuciones x86_64, pero puede verificarlo en /proc/timer_list y ver cuál es la resolución del reloj ‘ktime_get_real’.

  • Esta solución funciona perfectamente para mis necesidades. Lo probé en la plataforma ARM de 1 Ghz bajo Debian 7 ARM, el resultado es bastante consistente. Con el temporizador de 1 ms no tengo alarmas perdidas y WireShark muestra que el intervalo entre paquetes es cercano a 1 ms. ¡Muchas gracias por ayudar!

    –Xia

    22 de agosto de 2014 a las 14:47


Si desea que sea coherente, debe vincular su proceso a un núcleo y “quemar” ese núcleo girando en un ciclo ocupado con _mm_pause() en el fondo. Luego verificará el reloj en cada iteración del bucle y, si ha pasado la cantidad de tiempo correcta, disparará un paquete.

Si desea ser bastante consistente, debe contar el tiempo desde el principio y aumentar su variable “próximo tiempo de espera” cada vez que envíe un paquete. Pero tenga cuidado: sería fácil volverse loco cuando la computadora se pone a dormir más o menos, luego se vuelve a encender y piensa que es hora de disparar 100000 paquetes (solo pregúntele a Adobe, tuvieron este error en Flash hace aproximadamente una década) .

Ah, y obviamente no hagas esto en un dispositivo de energía limitada como una computadora portátil o un teléfono.

Puede implementar un componente de modo kernel para enviar el paquete UDP que puede evitar los cambios de contexto entre el modo kernel y el usuario. Al mismo tiempo, puede obtener acceso a un temporizador de alta precisión para enviar los paquetes casi en tiempo real.

  • Esto, básicamente algo como un controlador de tarjeta de red personalizado, es lo único que funcionará (al menos en la medida en que lo controle; si el cable físico está ocupado, aún no lo enviará a tiempo). De lo contrario, no tiene forma de saber cuándo saldrá su paquete, incluso si lo envía exactamente a tiempo.

    – Damon

    15 de agosto de 2014 a las 15:28

avatar de usuario
nish

Usa 2 hilos. Hilo principal

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

mientras tanto en hilo de trabajo

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

esto asegurará un poco que se envíe 1 paquete por intervalo de tiempo

avatar de usuario
grapas gabriel

Cómo ejecutar fácilmente un bucle periódico de alta resolución y alta precisión en Linux, a cualquier frecuencia (por ejemplo, hasta 10 KHz~100 KHz) utilizando un programador suave en tiempo real y retrasos de nanosegundos

Hacer esto bien es bastante complicado. Pero, con algunas funciones auxiliares que escribí, es realmente fácil. Entonces, déjame mostrarte la parte fácil primero.

Aquí se explica cómo configurar un bucle en Linux que se ejecuta a una frecuencia fija de 10 kilociclos (Pueden ser posibles frecuencias de hasta 10 KHz a 100 KHz, dependiendo de su código y sistema). Conseguir timinglib.h aquí.

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

¡Eso es todo! Súper simple. Pegue su código de socket para enviar paquetes UDP allí.

Tengo una demostración de socket UDP aquí:

  1. cliente
  2. servidor

Ahora, hablemos de las cosas de tiempo arriba.

sleep_until_us() funciona igual que el Función FreeRTOS vTaskDelayUntil(). Me gustó lo fácil que era de usar, así que hice que mi función actuara como tal en Linux.

Para hacerlo, debo usar clock_nanosleep() con la bandera TIMER_ABSTIME dormir hasta un tiempo absoluto que establezca en el futuro (en el período deseado), en lugar de por un tiempo relativo a partir de ahora.

Lea sobre eso aquí: https://man7.org/linux/man-pages/man2/clock_nanosleep.2.html

Vea mi llamada a esa función en mi implementación en 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);
        }
    }
}

Pero, eso no es suficiente. Con el Linux normal SCHED_OTHER programador, clock_nanosleep() tiene un intervalo de sueño mínimo de aproximadamente 55 nosotroscon un error hasta 1 ms. Eso no es lo suficientemente bueno para bucles de 1~10+ KHz. Entonces, activamos el SCHED_RR programador rotativo suave en tiempo real para obtener intervalos de sueño mínimos de aproximadamente 4 nosotroscon un error hasta 0,4 ms. Eso está mucho mejor.

Estudia mi respuesta aquí: Cómo dormir por unos microsegundos

Aquí está mi programador y código de configuración de bloqueo de memoria desde 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");
    }

}

Aquí hay una demostración completa con instrumentación de tiempo para probar las velocidades máximas posibles en su sistema: timinglib_pthread_periodic_loop.c.

Cuando se establece en un 100 nosotros período de bucle (10 kilociclos), aquí está la salida y el error. ¡Observa lo bueno que es el error! La mayoría de las iteraciones de bucle tienen un error de < 1 %, siendo el peor de los casos un error de +/- 20 % en ocasiones. Para 10 kilociclos¡genial!

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

Puedes ejecutarlo tú mismo. Clone mi repositorio y ejecute el código. Aquí está el comando gcc build and run, desde la parte superior del archivo:

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

Ver también:

  1. [my answer] Cómo dormir unos microsegundos y configurar SCHED_RR – Como configurar Linux SCHED_RR suave programador rotativo en tiempo real para que clock_nanosleep() puede tener una mejor resolución de ~4 nosotros abajo de ~55 nosotros
  2. [my answer] pthread_create no funciona correctamente con pthread_attr_setschedparam
  3. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad