0Pricing
C Academy · Lektion

Die Event Loop

Reagieren Sie auf lesbare Sockets.

Die Event Loop ist eine kostenlose C Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C Academy-Kurs umfasst insgesamt 4 Lektionen.

Das Herzstück des Servers

Die Ereignisschleife ist der Motor eines epoll-Servers. Sie wartet wiederholt auf bereite Deskriptoren und verteilt die Arbeit an Handler.

Ihr Aufbau ist immer gleich: epoll_wait() aufrufen, die zurückgegebenen Ereignisse durchlaufen, auf jedes reagieren und anschließend erneut von vorn beginnen. Alles Weitere baut auf diesem Grundgerüst auf.

for (;;) {
    int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
    for (int i = 0; i < n; i++) handle(&events[i]);
}

epoll_wait aufrufen

epoll_wait() erwartet den epoll-fd, ein Ausgabe-Array aus epoll_event, dessen Kapazität und ein Timeout in Millisekunden.

Der Aufruf blockiert, bis mindestens ein Deskriptor bereit ist oder das Timeout abläuft, und gibt zurück, wie viele Ereignisse in Ihr Array eingetragen wurden. Nur diese Einträge sind gültig.

struct epoll_event events[MAX_EVENTS];
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (n == -1) { perror("epoll_wait"); }

Das Timeout-Argument

Ein Timeout von -1 blockiert unbegrenzt, bis ein Ereignis eintrifft. Der Wert 0 kehrt sofort zurück und führt eine Abfrage ohne Wartezeit aus.

Ein positiver Wert begrenzt die Wartezeit. Das ist praktisch, wenn Sie zusätzlich regelmäßig Aufgaben ausführen müssen, etwa Logs leeren oder inaktive Verbindungen ablaufen lassen.

int n = epoll_wait(epfd, events, MAX_EVENTS, 1000); /* up to 1s */
if (n == 0) run_periodic_tasks();

Die Größe des Ereignis-Arrays festlegen

Die Kapazität des Arrays begrenzt, wie viele bereite Ereignisse ein Aufruf von epoll_wait() melden kann. Wenn weitere Ereignisse bereit sind, werden sie beim nächsten Aufruf zurückgegeben.

Eine moderate Größe wie 64 oder 1024 ist ausreichend. epoll wechselt bei aufeinanderfolgenden Aufrufen fair zwischen den ausstehenden Ereignissen, sodass keines dauerhaft zu kurz kommt.

#define MAX_EVENTS 64
struct epoll_event events[MAX_EVENTS];

Ereignisverteilung am Listening-Socket

Prüfen Sie innerhalb der Schleife zunächst, ob der bereite Deskriptor Ihr Listening-Socket ist. Wenn das der Fall ist, stehen neue Verbindungen aus.

Sie nehmen diese an, setzen sie auf Non-Blocking und registrieren sie bei epoll. Alle anderen Deskriptoren gehören zu bestehenden Clients, bei denen Daten gelesen oder Daten geschrieben werden können.

if (events[i].data.fd == listen_fd) {
    accept_new_connections(epfd, listen_fd);
} else {
    handle_client(epfd, &events[i]);
}

Verbindungen in einer Schleife annehmen

Ein Bereitschaftsereignis am Listening-Socket kann mehrere ausstehende Verbindungen bedeuten. Rufen Sie accept() in einer Schleife auf, bis EAGAIN zurückgegeben wird.

Das ist im Edge-Triggered-Modus unerlässlich und überall eine gute Praxis, damit Clients nicht bis zum nächsten Aufwecken warten müssen.

for (;;) {
    int c = accept(listen_fd, NULL, NULL);
    if (c == -1) { if (errno == EAGAIN) break; else break; }
    set_nonblocking(c);
    add_to_epoll(epfd, c);
}

Lesbare Ereignisse verarbeiten

Wenn bei einem Client EPOLLIN ausgelöst wird, lesen Sie von ihm. Im Level-Triggered-Modus ist ein einzelner recv()-Aufruf pro Aufwecken ausreichend.

Der Rückgabewert 0 bedeutet, dass der Client die Verbindung geschlossen hat: Räumen Sie sie auf. Ein negativer Rückgabewert mit EAGAIN bedeutet, dass Sie vorerst alle verfügbaren Daten gelesen haben.

ssize_t r = recv(fd, buf, sizeof buf, 0);
if (r == 0) { close_conn(epfd, fd); }
else if (r > 0) { process(buf, r); }

Im Edge-Triggered-Modus vollständig lesen

Mit EPOLLET müssen Sie lesen, bis recv() EAGAIN zurückgibt. Der Kernel benachrichtigt Sie nur beim Wechsel in den lesbaren Zustand; übrig gebliebene Bytes würden daher bis zur nächsten Änderung nicht verarbeitet.

Verpacken Sie den Lesevorgang in eine Schleife und verlassen Sie diese nur bei EAGAIN oder dem Ende des Datenstroms.

for (;;) {
    ssize_t r = recv(fd, buf, sizeof buf, 0);
    if (r > 0) process(buf, r);
    else if (r == 0) { close_conn(epfd, fd); break; }
    else { if (errno == EAGAIN) break; else { close_conn(epfd, fd); break; } }
}

Beschreibbare Ereignisse verarbeiten

EPOLLOUT wird ausgelöst, wenn ein Socket weitere ausgehende Daten aufnehmen kann. Sie benötigen dieses Ereignis nur, wenn gepufferte Bytes zuvor nicht vollständig gesendet werden konnten.

Leeren Sie Ihren Puffer. Sobald er leer ist, stellen Sie das Überwachungsinteresse mit EPOLL_CTL_MOD wieder auf ausschließlich EPOLLIN, um eine andauernde Flut von Schreibbereitschaftsereignissen zu vermeiden.

if (events[i].events & EPOLLOUT) {
    flush_pending(fd);
    if (buffer_empty(fd)) watch_read_only(epfd, fd);
}

Unterbrochene Wartevorgänge verarbeiten

epoll_wait() kann -1 mit errno == EINTR zurückgeben, wenn ein Signal den Aufruf unterbricht. Das ist kein echter Fehler; wiederholen Sie den Aufruf einfach.

Robuste Schleifen laufen bei EINTR weiter und behandeln nur andere Fehlercodes als schwerwiegend. Wenn Sie das vergessen, kann ein Server beim ersten empfangenen Signal abstürzen.

int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (n == -1) {
    if (errno == EINTR) continue;
    perror("epoll_wait"); break;
}

Eine Schleife, viele Clients

Zusammengeführt durchläuft ein einzelner Thread dauerhaft die bereiten Deskriptoren: annehmen, lesen, schreiben, schließen, wiederholen.

Da der Thread bei Inaktivität in epoll_wait() schläft und bei Auslastung nur bereite Arbeit verarbeitet, kann dieses Design problemlos Zehntausende gleichzeitiger Verbindungen bedienen.

Kurze Überprüfung

Überprüfen Sie Ihr Verständnis der Ereignisschleife.

Zusammenfassung

Die Ereignisschleife ruft epoll_wait() auf, durchläuft die bereiten Ereignisse und verteilt die Verarbeitung: Annehmen am Listening-Socket, Lesen bei EPOLLIN, Leeren des Puffers bei EPOLLOUT und Aufräumen bei einem Hangup.

Führen Sie Annahme- und Lesevorgänge in Schleifen aus, wiederholen Sie sie bei EINTR und lesen Sie unter EPOLLET vollständig. Als Nächstes setzen wir einen vollständigen Echo-Server zusammen.

Häufig gestellte Fragen

Ist die Lektion „Die Event Loop“ kostenlos?

Ja — der vollständige Text von „Die Event Loop“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Die Event Loop“?

Reagieren Sie auf lesbare Sockets. Du übst C Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um C Academy zu starten?

Keine Vorkenntnisse erforderlich. C Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Die Event Loop“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser C Academy-Lektion Code schreiben und ausführen?

Ja. Jede C Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Blockierendes und nicht blockierendes I/O
  2. epoll einrichten
  3. Die Event Loop
  4. Ein einfacher Echo-Server
← Zurück zu C Academy