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.
→ Standard Library · std.net.socket · 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
-
std.inotify — Dateisystemüberwachung (epoll-kompatibel)
-
-
std.thread — Threads mit Mutex/Bedingungsvariablen
Letzte Aktualisierung: 2026-06-05