====== std.net.epoll ======
epoll & I/O-Multiplexing (WP-5): Überwacht beliebig viele Dateideskriptoren auf Ereignisse (lesbar, schreibbar, Fehler) ohne Polling. Ein einzelner ''EpollWait''-Aufruf blockiert bis mindestens ein fd bereit ist und gibt nur die aktiven Events zurück. Enthält außerdem ''eventfd'' — eine thread-sichere fd-basierte Signalisierung.
→ [[lyx_-_programmiersprache:units|Standard Library]] · [[lyx_-_programmiersprache:units:net:socket|std.net.socket]] · [[lyx_-_programmiersprache:units:inotify|std.inotify]]
----
===== Konstanten =====
==== epoll_event-Struktur ====
^ Konstante ^ Wert ^ Beschreibung ^
| ''EPOLL_EVENT_SIZE'' | 12 | Größe eines ''epoll_event''-Eintrags in Bytes (+0: events uint32, +4: data uint64) |
==== Event-Bits (events-Feld) ====
^ Konstante ^ Wert ^ Beschreibung ^
| ''EPOLLIN'' | 1 | fd ist lesbar |
| ''EPOLLPRI'' | 2 | Dringende Daten vorhanden (OOB) |
| ''EPOLLOUT'' | 4 | fd ist schreibbar |
| ''EPOLLERR'' | 8 | Fehler aufgetreten (immer aktiv, kein explizites Setzen nötig) |
| ''EPOLLHUP'' | 16 | Hangup — Gegenseite hat Verbindung getrennt (immer aktiv) |
| ''EPOLLRDHUP'' | 8192 | Peer hat Schreib-Ende geschlossen (''EPOLLIN'' gefolgt von EOF) |
| ''EPOLLWAKEUP'' | 536870912 | Verhindert Systemsuspend während des Wartens |
| ''EPOLLONESHOT'' | 1073741824 | fd nach erstem Event automatisch aus epoll entfernen |
| ''EPOLLET'' | 2147483648 | Edge-Triggered: Event nur bei Zustandsänderung, nicht dauerhaft |
==== epoll_ctl-Operationen ====
^ Konstante ^ Wert ^ Beschreibung ^
| ''EPOLL_CTL_ADD'' | 1 | fd zur epoll-Instanz hinzufügen |
| ''EPOLL_CTL_DEL'' | 2 | fd aus epoll-Instanz entfernen |
| ''EPOLL_CTL_MOD'' | 3 | Überwachte Events für fd ändern |
==== Flags ====
^ Konstante ^ Wert ^ Beschreibung ^
| ''EPOLL_CLOEXEC'' | 524288 | epoll-fd bei ''exec()'' schließen |
| ''EFD_CLOEXEC'' | 524288 | eventfd-fd bei ''exec()'' schließen |
| ''EFD_NONBLOCK'' | 2048 | eventfd nicht-blockierend |
| ''EFD_SEMAPHORE'' | 1 | eventfd im Semaphore-Modus: ''EventFdRead'' dekrementiert um 1 statt Reset |
----
===== Funktionen =====
==== Instanz erstellen ====
^ Signatur ^ Beschreibung ^
| ''EpollCreate(): int64'' | Erstellt eine neue epoll-Instanz mit ''EPOLL_CLOEXEC''. Gibt den epoll-fd zurück, < 0 bei Fehler |
==== fd registrieren, ändern, entfernen ====
^ Signatur ^ Beschreibung ^
| ''EpollAdd(epFd: int64, fd: int64, events: int64, data: int64): int64'' | Registriert ''fd'' am epoll-fd. ''events'': Kombination aus ''EPOLLIN'', ''EPOLLOUT'', ''EPOLLET'' etc. ''data'': beliebiger uint64-Wert der bei Events zurückgeliefert wird (oft = ''fd'' selbst) |
| ''EpollMod(epFd: int64, fd: int64, events: int64, data: int64): int64'' | Ändert die überwachten Events für einen bereits registrierten ''fd'' |
| ''EpollDel(epFd: int64, fd: int64): int64'' | Entfernt ''fd'' aus der epoll-Instanz |
==== Auf Events warten ====
^ Signatur ^ Beschreibung ^
| ''EpollWait(epFd: int64, evBuf: int64, maxEvents: int64, timeoutMs: int64): int64'' | Wartet auf Events. ''evBuf'': ''alloc(EPOLL_EVENT_SIZE * maxEvents)''. ''timeoutMs'': -1 = blockieren, 0 = sofort zurück, >0 = Millisekunden. Gibt Anzahl aufgetretener Events zurück |
==== epoll_event-Felder auslesen ====
^ Signatur ^ Beschreibung ^
| ''EpollEventFlags(evBuf: int64, idx: int64): int64'' | Gibt das ''events''-Feld (uint32) des idx-ten Events zurück |
| ''EpollEventData(evBuf: int64, idx: int64): int64'' | Gibt das 8-Byte-Datenfeld des idx-ten Events zurück |
| ''EpollEventFd(evBuf: int64, idx: int64): int64'' | Gibt den fd aus dem Datenfeld zurück (untere 32 Bit von ''data'') |
==== eventfd — thread-sichere Signalisierung ====
^ Signatur ^ Beschreibung ^
| ''EventFdCreate(initVal: int64, flags: int64): int64'' | Erstellt einen eventfd-Zähler mit Startwert ''initVal''. ''flags'': ''EFD_CLOEXEC'' | ''EFD_NONBLOCK'' | ''EFD_SEMAPHORE'' (oder 0) |
| ''EventFdSignal(fd: int64): int64'' | Erhöht den Zähler um 1 — weckt alle wartenden ''EpollWait''- oder ''EventFdRead''-Aufrufe |
| ''EventFdRead(fd: int64): int64'' | Liest und resettet den Zähler (blockiert bei 0). Gibt den Zählerwert zurück |
----
===== Verwendung =====
==== Einfache Event-Loop (TCP-Server) ====
import std.net.epoll;
import std.net.socket;
import std.alloc;
import std.io;
fn RunLoop(listenFd: int64): void {
var ep := EpollCreate();
EpollAdd(ep, listenFd, EPOLLIN, listenFd);
var buf: int64 := alloc(EPOLL_EVENT_SIZE * 64);
var running: bool := true;
while (running) {
var n := EpollWait(ep, buf, 64, -1);
var i: int64 := 0;
while (i < n) {
var fd := EpollEventFd(buf, i);
var flags := EpollEventFlags(buf, i);
if (fd == listenFd) {
var clientFd := Accept(listenFd, 0, 0);
EpollAdd(ep, clientFd, EPOLLIN, clientFd);
} else {
if ((flags & EPOLLIN) != 0) {
// Daten vom Client lesen...
}
if ((flags & EPOLLHUP) != 0) {
EpollDel(ep, fd);
close(fd);
}
}
i := i + 1;
}
}
free(buf, EPOLL_EVENT_SIZE * 64);
close(ep);
}
==== inotify + epoll kombinieren ====
import std.net.epoll;
import std.inotify;
import std.alloc;
fn WatchWithEpoll(path: pchar): void {
var ep := EpollCreate();
var ifd := InotifyCreate();
InotifyWatch(ifd, path, IN_CREATE | IN_MODIFY | IN_DELETE);
// inotify-fd in epoll registrieren
EpollAdd(ep, ifd, EPOLLIN, ifd);
var evBuf: int64 := alloc(EPOLL_EVENT_SIZE * 8);
var inBuf: int64 := alloc(4096);
var n := EpollWait(ep, evBuf, 8, 5000); // max. 5 Sekunden warten
if (n > 0) {
var bytes := InotifyRead(ifd, inBuf, 4096);
// Events verarbeiten...
}
free(evBuf, EPOLL_EVENT_SIZE * 8);
free(inBuf, 4096);
close(ifd);
close(ep);
}
==== eventfd: Thread wecken ====
import std.net.epoll;
import std.thread;
// Thread A: wartet auf Signal
fn Worker(efd: int64): void {
var ep := EpollCreate();
EpollAdd(ep, efd, EPOLLIN, efd);
var buf: int64 := alloc(EPOLL_EVENT_SIZE);
EpollWait(ep, buf, 1, -1); // blockiert bis Signal
EventFdRead(efd); // Zähler zurücksetzen
free(buf, EPOLL_EVENT_SIZE);
close(ep);
}
// Thread B: sendet Signal
fn Notifier(efd: int64): void {
// ... Arbeit erledigen ...
EventFdSignal(efd); // weckt Worker
}
fn Main(): void {
var efd := EventFdCreate(0, EFD_CLOEXEC);
// Threads starten, efd übergeben...
close(efd);
}
==== EPOLLONESHOT: einmaliges Event ====
import std.net.epoll;
import std.alloc;
// fd wird nach erstem Event automatisch aus epoll entfernt.
// Für erneutes Warten: EpollMod mit EPOLLIN | EPOLLONESHOT aufrufen.
fn WaitOnce(fd: int64): void {
var ep := EpollCreate();
EpollAdd(ep, fd, EPOLLIN | EPOLLONESHOT, fd);
var buf: int64 := alloc(EPOLL_EVENT_SIZE);
EpollWait(ep, buf, 1, -1);
free(buf, EPOLL_EVENT_SIZE);
close(ep);
}
----
===== Hinweise =====
* Im Edge-Triggered-Modus (''EPOLLET'') muss der fd bis zum ''EAGAIN'' vollständig geleert werden — sonst werden keine weiteren Events gemeldet.
* ''EPOLLERR'' und ''EPOLLHUP'' sind immer aktiv und müssen nicht explizit in ''events'' gesetzt werden.
* ''EPOLLONESHOT'' erfordert nach jedem Event ein ''EpollMod'', um den fd wieder zu aktivieren.
* Ein eventfd-fd kann selbst in eine epoll-Instanz eingehängt werden und wird bei ''EPOLLIN'' angezeigt sobald der Zähler > 0 ist.
* ''EpollEventData'' und ''EpollEventFd'' lesen denselben Offset — ''EpollEventFd'' maskiert nur auf 32 Bit.
----
===== Verwandte Units =====
* ''[[lyx_-_programmiersprache:units:net:socket|std.net.socket]]'' — TCP/UDP-Sockets
* ''[[lyx_-_programmiersprache:units:inotify|std.inotify]]'' — Dateisystemüberwachung (epoll-kompatibel)
* ''[[lyx_-_programmiersprache:units:mqueue|std.mqueue]]'' — Message Queues (epoll-kompatibel)
* ''[[lyx_-_programmiersprache:units:signals|std.signals]]'' — signalfd (epoll-kompatibel)
* ''[[lyx_-_programmiersprache:units:thread|std.thread]]'' — Threads mit Mutex/Bedingungsvariablen
Letzte Aktualisierung: 2026-06-05