GNU/Linux >> Tutoriales Linux >  >> Linux

Crear un temporizador en Linux

La sincronización de ciertos eventos es una tarea común para un desarrollador. Los escenarios comunes para los temporizadores son los perros guardianes, la ejecución cíclica de tareas o la programación de eventos para un tiempo específico. En este artículo, muestro cómo crear un temporizador de intervalo compatible con POSIX usando timer_create(...).

Puede descargar el código fuente de los siguientes ejemplos desde GitHub.

Preparar Qt Creator

Usé Qt Creator como IDE para este ejemplo. Para ejecutar y depurar el código de ejemplo en Qt Creator, clone el repositorio de GitHub, abra Qt Creator y vaya a Archivo -> Abrir archivo o proyecto... y elija CMakeLists.txt :

Después de seleccionar la cadena de herramientas, haga clic en Configurar proyecto . El proyecto contiene tres ejemplos independientes (solo cubriremos dos de ellos en este artículo). Con el menú marcado en verde, cambie entre las configuraciones para cada ejemplo y active Ejecutar en terminal para cada uno de ellos (ver la marca amarilla a continuación). El ejemplo actualmente activo para compilar y depurar se puede seleccionar sobre Depurar botón en la esquina inferior izquierda (ver la marca naranja a continuación):

Temporizador de enhebrado

Echemos un vistazo a simple_threading_timer.c ejemplo. Este es el más simple:muestra cómo se crea un temporizador de intervalos, que llama a la función caducada al vencimiento. En cada vencimiento, se crea un nuevo hilo en el que la función expiration se llama.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

void expired(union sigval timer_data);

pid_t gettid(void);

struct t_eventData{
    int myData;
};

int main()
{
    int res = 0;
    timer_t timerId = 0;

    struct t_eventData eventData = { .myData = 0 };


    /*  sigevent specifies behaviour on expiration  */
    struct sigevent sev = { 0 };

    /* specify start delay and interval
     * it_value and it_interval must not be zero */

    struct itimerspec its = {   .it_value.tv_sec  = 1,
                                .it_value.tv_nsec = 0,
                                .it_interval.tv_sec  = 1,
                                .it_interval.tv_nsec = 0
                            };

    printf("Simple Threading Timer - thread-id: %d\n", gettid());

    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = &expired;
    sev.sigev_value.sival_ptr = &eventData;


    /* create timer */
    res = timer_create(CLOCK_REALTIME, &sev, &timerId);


    if (res != 0){
        fprintf(stderr, "Error timer_create: %s\n", strerror(errno));
        exit(-1);
    }

    /* start timer */
    res = timer_settime(timerId, 0, &its, NULL);

    if (res != 0){
        fprintf(stderr, "Error timer_settime: %s\n", strerror(errno));
        exit(-1);
    }

    printf("Press ETNER Key to Exit\n");
    while(getchar()!='\n'){}
    return 0;
}


void expired(union sigval timer_data){
    struct t_eventData *data = timer_data.sival_ptr;
    printf("Timer fired %d - thread-id: %d\n", ++data->myData, gettid());
}

La ventaja de este enfoque es su pequeño tamaño, en términos de código y depuración simple. La desventaja es la sobrecarga adicional debido a la creación de un nuevo subproceso al vencimiento y, en consecuencia, el comportamiento menos determinista.

Más recursos de Linux

  • Hoja de trucos de los comandos de Linux
  • Hoja de trucos de comandos avanzados de Linux
  • Curso en línea gratuito:Descripción general técnica de RHEL
  • Hoja de trucos de red de Linux
  • Hoja de trucos de SELinux
  • Hoja de trucos de los comandos comunes de Linux
  • ¿Qué son los contenedores de Linux?
  • Nuestros últimos artículos sobre Linux

Temporizador de señal de interrupción

Otra posibilidad de ser notificado por un temporizador expirado se basa en una señal del kernel. En lugar de crear un nuevo subproceso cada vez que expira el temporizador, el kernel envía una señal al proceso, el proceso se interrumpe y se llama al controlador de señal correspondiente.

Como la acción predeterminada al recibir una señal es finalizar el proceso (consulte la página del manual de señales), debemos preparar Qt Creator con anticipación para que sea posible una depuración adecuada.

El comportamiento predeterminado de Qt Creator cuando el depurado recibe una señal es:

  • Interrumpir la ejecución y cambiar al contexto del depurador.
  • Muestra una ventana emergente que notifica al usuario sobre la recepción de una señal.

Ambas acciones no son deseadas ya que la recepción de una señal es parte de nuestra aplicación.

Qt Creator usa GDB en segundo plano. Para evitar que GDB detenga la ejecución cuando el proceso recibe una señal, vaya a Herramientas -> Opciones , seleccione Depurador y vaya a Locals &Expressions . Agregue la siguiente expresión a Personalización del asistente de depuración :

handle SIG34 nostop pass

Puede encontrar más información sobre el manejo de señales de GDB en la documentación de GDB.

A continuación, queremos suprimir la ventana emergente que nos notifica cada vez que se recibe una señal cuando nos detenemos en el controlador de señales:

Para hacerlo, navegue a la pestaña GDB y desmarque la casilla marcada:

Ahora puede depurar correctamente el signal_interrupt_timer . La implementación real del temporizador de señal es un poco más compleja:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#define UNUSED(x) (void)(x)

static void handler(int sig, siginfo_t *si, void *uc);
pid_t gettid(void);

struct t_eventData{
    int myData;
};

int main()
{
    int res = 0;
    timer_t timerId = 0;


    struct sigevent sev = { 0 };
    struct t_eventData eventData = { .myData = 0 };

    /* specifies the action when receiving a signal */
    struct sigaction sa = { 0 };

    /* specify start delay and interval */
    struct itimerspec its = {   .it_value.tv_sec  = 1,
                                .it_value.tv_nsec = 0,
                                .it_interval.tv_sec  = 1,
                                .it_interval.tv_nsec = 0
                            };

    printf("Signal Interrupt Timer - thread-id: %d\n", gettid());

    sev.sigev_notify = SIGEV_SIGNAL; // Linux-specific
    sev.sigev_signo = SIGRTMIN;
    sev.sigev_value.sival_ptr = &eventData;

    /* create timer */
    res = timer_create(CLOCK_REALTIME, &sev, &timerId);

    if ( res != 0){
        fprintf(stderr, "Error timer_create: %s\n", strerror(errno));
        exit(-1);
    }

    /* specifz signal and handler */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handler;

    /* Initialize signal */
    sigemptyset(&sa.sa_mask);

    printf("Establishing handler for signal %d\n", SIGRTMIN);

    /* Register signal handler */
    if (sigaction(SIGRTMIN, &sa, NULL) == -1){
        fprintf(stderr, "Error sigaction: %s\n", strerror(errno));
        exit(-1);
    }

    /* start timer */
    res = timer_settime(timerId, 0, &its, NULL);

    if ( res != 0){
        fprintf(stderr, "Error timer_settime: %s\n", strerror(errno));
        exit(-1);
    }

    printf("Press ENTER to Exit\n");
    while(getchar()!='\n'){}
    return 0;
}



static void
handler(int sig, siginfo_t *si, void *uc)
{
    UNUSED(sig);
    UNUSED(uc);
    struct t_eventData *data = (struct t_eventData *) si->_sifields._rt.si_sigval.sival_ptr;
    printf("Timer fired %d - thread-id: %d\n", ++data->myData, gettid());
}

A diferencia del temporizador de subprocesamiento, tenemos que inicializar la señal y registrar un controlador de señal. Este enfoque es más eficaz ya que no provocará la creación de subprocesos adicionales. Por esta razón, la ejecución del manejador de señales también es más determinista. El inconveniente es claramente el esfuerzo de configuración adicional para depurar esto correctamente.

Resumen

Ambos métodos descritos en este artículo son implementaciones de temporizadores cercanas al kernel. Incluso si la función timer_create(...) es parte de la especificación POSIX, no es posible compilar el código de muestra en un sistema FreeBSD debido a las pequeñas diferencias en las estructuras de datos. Además de este inconveniente, dicha implementación le brinda un control detallado para aplicaciones de temporización de uso general.


Linux
  1. Cree una bóveda de archivos cifrados en Linux

  2. Comando Linux ln

  3. Crear un archivo de intercambio de Linux

  4. Cambiar el temporizador del kernel de Linux

  5. ¿Cómo crear un temporizador de alta resolución en Linux para medir el rendimiento del programa?

Cómo crear accesos directos en el escritorio de Linux

Cómo crear un alias SSH en Linux

Cómo crear un alias en Linux

4 formas de crear un nuevo archivo en Linux

El comando del temporizador en Linux

Crear una partición en Linux:una guía paso a paso