====== 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