GNU/Linux >> Znalost Linux >  >> Linux

Vytvořte časovač v systému Linux

Načasování určitých událostí je pro vývojáře běžným úkolem. Běžnými scénáři pro časovače jsou hlídací psi, cyklické provádění úkolů nebo plánování událostí na konkrétní čas. V tomto článku ukážu, jak vytvořit intervalový časovač vyhovující standardu POSIX pomocí timer_create(...).

Zdrojový kód pro následující příklady si můžete stáhnout z GitHubu.

Připravte Qt Creator

Pro tento příklad jsem jako IDE použil Qt Creator. Chcete-li spustit a ladit ukázkový kód v Qt Creator, naklonujte úložiště GitHub, otevřete Qt Creator a přejděte na Soubor -> Otevřít soubor nebo projekt... a vyberte soubor CMakeLists.txt :

Po výběru sady nástrojů klikněte na Konfigurovat projekt . Projekt obsahuje tři nezávislé příklady (v tomto článku se budeme zabývat pouze dvěma z nich). Pomocí zeleně označené nabídky přepínejte mezi konfiguracemi pro každý příklad a aktivujte Spustit v terminálu pro každý z nich (viz žlutá značka níže). Aktuálně aktivní příklad pro sestavení a ladění lze vybrat v části Ladění tlačítko v levém dolním rohu (viz oranžová značka níže):

Časovač vláken

Pojďme se podívat na simple_threading_timer.c příklad. Toto je nejjednodušší:Ukazuje, jak se vytváří intervalový časovač, který volá funkci expired při vypršení platnosti. Při každém vypršení platnosti se vytvoří nové vlákno, ve kterém je funkce vypršení se nazývá.

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

Výhodou tohoto přístupu je jeho malé rozměry, pokud jde o kód a jednoduché ladění. Nevýhodou je dodatečná režie kvůli vytvoření nového vlákna při expiraci a následně méně deterministické chování.

Další zdroje pro Linux

  • Cheat pro příkazy Linuxu
  • Cheat sheet pro pokročilé příkazy systému Linux
  • Bezplatný online kurz:Technický přehled RHEL
  • Síťový cheat pro Linux
  • Cheat sheet SELinux
  • Cheat pro běžné příkazy pro Linux
  • Co jsou kontejnery systému Linux?
  • Naše nejnovější články o Linuxu

Časovač signálu přerušení

Další možnost upozornění vypršelým časovačem je založena na signálu jádra. Místo vytváření nového vlákna pokaždé, když vyprší časovač, jádro vyšle signál procesu, proces se přeruší a zavolá se odpovídající obsluha signálu.

Protože výchozí akcí při příjmu signálu je ukončit proces (viz manuálová stránka signálu), musíme si Qt Creator připravit předem, aby bylo možné správně ladit.

Výchozí chování Qt Creatoru, když debuggee přijme signál, je:

  • Přerušte provádění a přepněte do kontextu ladicího programu.
  • Zobrazte vyskakovací okno, které uživatele upozorní na příjem signálu.

Obě akce nejsou žádoucí, protože příjem signálu je součástí naší aplikace.

Qt Creator používá GDB na pozadí. Chcete-li zabránit GDB v zastavení provádění, když proces obdrží signál, přejděte na Nástroje -> Možnosti , vyberte Ladicí program a přejděte na Místní lidé a výrazy . Přidejte následující výraz do Přizpůsobení pomocníka pro ladění :

handle SIG34 nostop pass

Další informace o zpracování signálu GDB naleznete v dokumentaci GDB.

Dále chceme potlačit vyskakovací okno, které nás upozorní pokaždé, když je přijat signál, když zastavíme v obsluze signálu:

Chcete-li tak učinit, přejděte na kartu GDB a zrušte zaškrtnutí označeného políčka:

Nyní můžete správně odladit signal_interrupt_timer . Vlastní implementace časovače signálu je o něco složitější:

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

Na rozdíl od časovače vláken musíme signál inicializovat a zaregistrovat obsluhu signálu. Tento přístup je výkonnější, protože nezpůsobí vytváření dalších vláken. Z tohoto důvodu je také provedení obsluhy signálu determinističtější. Nevýhodou je zjevně zvláštní úsilí o konfiguraci, které je nutné správně odladit.

Shrnutí

Obě metody popsané v tomto článku jsou implementace časovačů blízké jádru. I když je funkce timer_create(...) součástí specifikace POSIX, není možné zkompilovat ukázkový kód na systému FreeBSD kvůli malým rozdílům v datových strukturách. Kromě této nevýhody vám taková implementace poskytuje jemné ovládání pro všeobecné aplikace časování.


Linux
  1. Vytvořte šifrovanou úschovnu v systému Linux

  2. Jak vytvořit skript příkazu Linux

  3. Jak vytvořit balíček RPM pro Linux

  1. Jak vytvořit swap v Linuxu

  2. Jak vytvořit službu Systemd v Linuxu

  3. Příkaz Linux ln

  1. Vytvořte odkládací soubor Linuxu

  2. Změňte časovač linuxového jádra

  3. Jak vytvořit časovač s vysokým rozlišením v Linuxu pro měření výkonu programu?