Forum: PC-Programmierung Events mit timerfd und epoll


von Bern (Gast)


Lesenswert?

Hallo Zusammen,
kann mir jemand als Anfänger etwas zum Thema timerfd und epoll sagen? 
Habe im Netz ein paar Beispiele gefunden wie unter 
http://man7.org/linux/man-pages/man2/timerfd_create.2.html, aber 
verstehe nicht ganz, wie das funktioniert.

Ich habe versucht daraus was eigenes zu basteln. Dabei ist mir 
aufgefallen, dass ndie Zeit, die ich festlege (z.B. 
new_time.it_interval.tv_sec = 10) nach unter wie ein Countdown zählt.

ich habe es soweit hinbekommen, dass bei jedem Tastendruck oder 
Mausbewegung die Zeit auf dem Bildschirm ausgegeben wird. Aber irgendwie 
bin ich mit dem Thema etwas überfordert. Eventuell hat jemand Erfahrung 
damit?

von Clemens L. (c_l)


Lesenswert?

Ich weiß nicht, was das Ziel deines Programms ist, oder was falsch 
läuft.

Hast du eine konkrete Frage?

von Bernd (Gast)


Lesenswert?

Ich bin mittlerweile weitergekommen, auch wenn das keine leichte Kost 
ist.
1
/* Print "Beep!" every second and the number of signals that are received. */
2
/* Send `kill -9` to stop this program. */
3
#include <inttypes.h>
4
#include <signal.h>
5
#include <stdio.h>
6
#include <sys/epoll.h>
7
#include <sys/timerfd.h>
8
#include <sys/signalfd.h>
9
#include <unistd.h>
10
11
int main(void)
12
{
13
    int efd, sfd, tfd;
14
    struct itimerspec timer = {
15
        .it_interval = {1, 0},  /* 1 second */
16
        .it_value    = {1, 0},
17
    };
18
    uint64_t count;
19
    sigset_t sigmask;
20
    struct signalfd_siginfo siginfo;
21
#define MAX_EVENTS 2
22
    struct epoll_event ev, events[MAX_EVENTS];
23
    ssize_t nr_events;
24
    size_t i;
25
26
    /* No error checking! */
27
    tfd = timerfd_create(CLOCK_MONOTONIC, 0);
28
    timerfd_settime(tfd, 0, &timer, NULL);
29
30
    sigfillset(&sigmask);
31
    sigprocmask(SIG_BLOCK, &sigmask, NULL);
32
    sfd = signalfd(-1, &sigmask, 0);
33
34
    efd = epoll_create1(0);
35
36
    ev.events = EPOLLIN;
37
    ev.data.fd = tfd;
38
    epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &ev);
39
40
    ev.events = EPOLLIN;
41
    ev.data.fd = sfd;
42
    epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &ev);
43
44
    for (;;) {
45
        nr_events = epoll_wait(efd, events, MAX_EVENTS, -1);
46
        for (i = 0; i < nr_events; i++) {
47
            if (events[i].data.fd == tfd) {
48
                read(tfd, &count, sizeof(count));
49
                printf("Beep!\n");
50
            } else if (events[i].data.fd == sfd) {
51
                read(sfd, &siginfo, sizeof(siginfo));
52
                printf("Received signal number %d\n", siginfo.ssi_signo);
53
            }
54
        }
55
    }
56
    return 0;
57
}

was mich jetzt interessieren würde, ist der Eventhandler. Wozu wird es 
eigentlich benötigt denn in diesem Fall hier könnte man dich auch 
einfach ohne poll/epoll arbeiten und direkt über die Funktionen die 
Inhalte für eine Verzweigung beziehen.

von Clemens L. (c_l)


Lesenswert?

Bernd schrieb:
> was mich jetzt interessieren würde, ist der Eventhandler.

Mich auch. Was meinst du mit "Eventhandler"?

> in diesem Fall hier könnte man dich auch einfach ohne poll/epoll arbeiten
> und direkt über die Funktionen die Inhalte für eine Verzweigung beziehen.

Welche Funktionen? Wie soll das gehen?

: Bearbeitet durch User
von Bernd (Gast)


Lesenswert?

Na ja, was bringt einem ein Event Handler, um beispielsweise auf Signale 
oder tastatureingaben zu reagieren? Das kann man doch auch mit einfachen 
bedingungen lösen.

von AntiMaker (Gast)


Lesenswert?

Bernd schrieb:
> um beispielsweise auf Signale
> oder tastatureingaben zu reagieren?

Andersherum macht es mehr Sinn.

epoll & co erlauben es dem GUI-Framework deiner Wahl eine effiziente 
Event-Loop mit Timern und allem Pipapo zu implementieren. Die Details 
sind dann schön versteckt, und du kannst ganz High-Level mit 
"onButtonPressed" etc arbeiten.


Bernd schrieb:
> Das kann man doch auch mit einfachen
> bedingungen lösen.

Mit Busy waiting? Damit die CPU ständig 150Watt verbrät, nur um auf 
Tasteneingaben zu warten? und alle anderen Programme keine CPU-Zeit mehr 
bekommen?

Unter DOS war das noch so üblich. Da gab's aber auch kein Multitasking 
und keine ausgefeilten Stromsparoptionen in den CPUs.

von Bernd (Gast)


Lesenswert?

Ich hätte zum dem Code noch eine Frage und zwar hier:         nr_events 
= epoll_wait(efd, events, MAX_EVENTS, -1);

Und hier die Beschreibung der Parameter: " The memory area
       pointed to by events will contain the events that will be 
available
       for the caller.  Up to maxevents are returned by epoll_wait(). "

Wenn ich das also richtig verstehe, kann ich mir mehrere Events 
erzeugen, die ich dann über die Funktion "epoll_ctl" registriere. Dabei 
werden diese in eine Art Liste bzw. Array abgelegt. Und die 
Struktur-Instanz "events[MAX_EVENTS]" dient dann dazu, dass ich darin 
sozusagen meine Events speichere, auf die ich dann später zugreifen 
kann. Ist das richtig?

Wieso wird in dem Beispiel oben eigentlich ein Array mit zwei Events 
verwendet?

von Clemens L. (c_l)


Lesenswert?

Bernd schrieb:
> Wenn ich das also richtig verstehe, kann ich mir mehrere Events
> erzeugen, die ich dann über die Funktion "epoll_ctl" registriere. Dabei
> werden diese in eine Art Liste bzw. Array abgelegt. Und die
> Struktur-Instanz "events[MAX_EVENTS]" dient dann dazu, dass ich darin
> sozusagen meine Events speichere, auf die ich dann später zugreifen
> kann.

Hier verwechselst du "Event" und "Event".

Mit EPOLL_CTL_ADD registrierst du eine Quelle von möglichen Events.

Das Array, das du an epoll() übergibst, werden eingetretene Ereignisse 
gespeichert. Die Größe dieses Arrays muss nicht mit der Anzahl der 
registrierten FDs übereinstimmen.

> Wieso wird in dem Beispiel oben eigentlich ein Array mit zwei Events
> verwendet?

Solange du keinen Server mit tausenden Ereignissen pro Minute hast, ist 
das eigentlich egal.

von Bernd (Gast)


Lesenswert?

Ich registriere eine Quelle, also in dem Fall die Instanz epollfd. Aber 
die nacheinander registrierten Events werden intern doch anscheinend 
nacheinander irgendwie abgelegt. Ich habe ja anscheinend zwei 
indexnummer, also 3 und 4. Das heisst mir jeder Registrierung wird das 
entsprechende event abgelegt und mit einer Index nummer versehen. Und 
mithilfe des file descriptors und epoll_wait speichere ich diese events 
in dem array.

Ich verstehe dennoch nicht, in wie weit das Array und die if else eine 
Rolle spielen. Ohne if/else wird zeit erst ausgegeben,wenn auch ein 
Signal eintrifft, da read() dafür sorgt, dass eben auf Signal gewartet 
wird, bis zu read von timer gesprungen wird. Aber wieso zwei Felder für 
das Array. Was steht in Feld 0 und was in Feld 1?

von Clemens L. (c_l)


Lesenswert?

Bernd schrieb:
> Ich registriere eine Quelle, also in dem Fall die Instanz epollfd.

Nein. Die Quellen sind tfd und sfd.

> Aber die nacheinander registrierten Events werden intern doch anscheinend
> nacheinander irgendwie abgelegt. Ich habe ja anscheinend zwei
> indexnummer, also 3 und 4.

Der Zahlenwert von File-Handles ist nicht relevant.

> Das heisst mir jeder Registrierung wird das entsprechende event abgelegt
> und mit einer Index nummer versehen.

Nein. tfd und sfd wurden vorher angelegt. Die Registrierung mit 
epoll_ctl() erzeugt keinen Index.

> Und mithilfe des file descriptors und epoll_wait speichere ich diese
> events in dem array.

Nein. epoll() schreibt genau dann etwas in das Array, wenn ein Ereignis 
wirklich passiert (also wenn der Timer abläuft, oder wenn ein Signal 
ankommt).

> Ich verstehe dennoch nicht, in wie weit das Array und die if else eine
> Rolle spielen.

Wenn der Timer abläuft, dann schreibt epoll() den Wert von tfd in den 
ersten Eintrag des Arrays, und liefert 1 zurück.

Wenn ein Signal ankommt, dann schreibt epoll() den Wert von sfd in den 
ersten Eintrag des Arrays, und liefert 1 zurück.

Nur wenn Timer und Signal zufälligerweise gleichzeitig passieren (oder 
seit dem letzten Aufruf von epoll() passiert sind, weil dein Code zu 
lange rumgetrödelt hat), dann werden tfd und sfd in die beiden Einträge 
des Arrays geschrieben (die Reihenfolge weißt du nicht vorher), und 
epoll() liefert 2 zurück.

von Bernd (Gast)


Lesenswert?

Das nenne ich mal eine verständliche Erklärung. Ich habe mich weiter 
oben nicht falsch ausgedrückt.

epollfd ist im Prinzip mein FD, der auf die EPOLL-Instanz referenziert. 
In meinem Fall mit Index = 5;

Dann Initialisiere ich die event-Struktur mit den jeweiligen FDs von 
Timer und Signal und registriere diese beiden FDs, die eben Quelle für 
Events sind.

mit epoll_wait warte ich anschließend, bis mindesten 1 Event auftritt, 
in dem Fall Timer oder Signal. Wenn ja, wird Wert, auf den tfd/sfd 
referenziert, in das Array geschrieben und kann dann behandelt werden. 
In dem Fall ausgegeben.

Das Thema ist an sich nicht schwer, aber als Linux-Neuling zumindest auf 
den ersten Blick keine leichte Kost. Wenn man das Prinzip aber einmal 
verstanden hat, ist das garnicht so schwer.

Das ist genau wie mit dem Thema timerfd. Wird Beispielsweise das Flag 
"TFD_TIMER_ABSTIME" verwendet, muss ich bei der Initialisierung von 
xyz.it_value.tv.sec zu meinem Wert (z.B. 5s) über getclock die Zeit 
(z.B. 50984833) dazurechnen. Ohne das Flag könnte ich mit normalen 
Werten ohne getclock rechnen. Aber das erstmal in den Kopf zu bekommen, 
ist schwierig.

von Rolf M. (rmagnus)


Lesenswert?

Bernd schrieb:
> Das nenne ich mal eine verständliche Erklärung. Ich habe mich weiter
> oben nicht falsch ausgedrückt.
>
> epollfd ist im Prinzip mein FD, der auf die EPOLL-Instanz referenziert.

Ein "epollfd" kommt oben gar nicht vor. Ich vermute, du meinst efd.

> In meinem Fall mit Index = 5;

Was meinst du mit Index? Es gibt da nirgends einen Index.

> Dann Initialisiere ich die event-Struktur mit den jeweiligen FDs von
> Timer und Signal und registriere diese beiden FDs, die eben Quelle für
> Events sind.

Ja.

> mit epoll_wait warte ich anschließend, bis mindesten 1 Event auftritt,
> in dem Fall Timer oder Signal. Wenn ja, wird Wert, auf den tfd/sfd
> referenziert, in das Array geschrieben und kann dann behandelt werden.
> In dem Fall ausgegeben.

Ja.

> Das ist genau wie mit dem Thema timerfd. Wird Beispielsweise das Flag
> "TFD_TIMER_ABSTIME" verwendet, muss ich bei der Initialisierung von
> xyz.it_value.tv.sec zu meinem Wert (z.B. 5s) über getclock die Zeit
> (z.B. 50984833) dazurechnen. Ohne das Flag könnte ich mit normalen
> Werten ohne getclock rechnen. Aber das erstmal in den Kopf zu bekommen,
> ist schwierig.

Mir ist ehrlich gesagt nicht klar, was daran schwierig ist. Das Flag ist 
doch gerade dafür da, um eine absolute Zeit angeben zu können. Das 
verwendest du doch eigentlich auch nur dann, wenn du genau das willst. 
Da ist es doch ziemlich logisch, dass man das dann auch tun muss.

von Clemens L. (c_l)


Lesenswert?

Rolf M. schrieb:
>> In meinem Fall mit Index = 5;
>
> Was meinst du mit Index? Es gibt da nirgends einen Index.

POSIX sagt, dass ein File-Handle ein Index in die interne Handle-Tabelle 
ist, und garantiert, dass ein neues Handle den ersten freien Eintrag 
belegt. Das ist aber eher historisch und ist zum Verständnis nicht 
unbedingt notwendig.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Clemens L. schrieb:
> Rolf M. schrieb:
>>> In meinem Fall mit Index = 5;
>>
>> Was meinst du mit Index? Es gibt da nirgends einen Index.
>
> POSIX sagt, dass ein File-Handle ein Index in die interne Handle-Tabelle
> ist, und garantiert, dass ein neues Handle den ersten freien Eintrag
> belegt. Das ist aber eher historisch und ist zum Verständnis nicht
> unbedingt notwendig.

Naja, ok. Dass das ein Index in eine interne Tabelle ist, ist eigentlich 
klar. Das ist ja die offensichtliche Art, das zu implementieren. Als 
Benutzer der API ist mir das aber herzlich egal. Ich betrachte es 
einfach als ein opakes Ding, das ich von irgendwelchen 
Erzeugungsfunktionen von POSIX bekomme und den anderen POSIX-Funktionen 
übergeben muss, damit sie wissen, welches File/Socket/wasauchimmer ich 
jetzt gerade meine. Welcher Wert da drin steht, ist mir dabei egal, 
genauso wie es mir egal ist, was in einem FILE* oder einem pthread_t 
steht.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.