Hi Leute,
ich wollte euch nur mitteilen, daß die Umstellung auf poll() das Problem gelöst hat. Ich bin auf folgende Hürden gestoßen, die es zu umschiffen galt:
Erstmal war es notwendig, wie schon gesagt, die maximalen Filehandles pro Prozess anzuheben. Dazu reichte es, mit setrlimit() das entsprechende Feld zu setzen
Code:
struct rlimit rl;
rl.rlim_cur = 4096;
rl.rlim_max = 8192;
setrlimit(RLIMIT_NOFILE, &rl);
Ob jetzt rlim_cur und rlim_max gleich sein sollten oder nicht, hab ich nicht getestet. Es funktioniert so, wie es hier steht, und das reicht mir.
Das nächste war die Umstellung von select() auf poll() selbst. Dazu hab ich drei Funktionen und ein statisches Array angelegt. Das Array besteht aus einer Menge Elementen vom Typ struct pollfd, entsprechend der gesetzten Anzahl in rlim_cur. Anfänglich wurden von mir alle Filehandles in diesem Array (Feld 'fd' der Struktur pollfd) auf -1 gesetzt, um sie für poll() zu deaktivieren. Die Funktionen dienen nun dazu, Filehandles in diesem Array zu aktivieren und entsprechende Überwachungs-Flags (z.B. POLLIN, POLLOUT) zu setzen, bzw. zu deaktivieren/löschen. Eine dritte Funktion fragt das Element eines Arrays anhand des Filehandles auf ein bestimmtes Überwachungs-Flag ab.
Code:
int inline Poll(int interval)
{
int retval = ::poll(poll_handles, fd_max+1, interval);
return retval;
}
void inline AddToFDSet(SHANDLE fd, int mode) // fd = FileHandle, mode = POLLIN/POLLOUT
{
poll_handles[fd].fd = fd;
poll_handles[fd].events |= mode; // Flag setzen
}
void inline RmFromPollFDs(SHANDLE fd, int mode) // fd = FileHandle, mode = POLLIN/POLLOUT
{
poll_handles[fd].events = (poll_handles[fd].events & ~mode); // Flag ausschalten
if(poll_handles[fd].events == 0) // Wenn alle Flags in events gelöscht werden, kann auch das fd entfernt werden (Funktionalität deaktiviert)
{
poll_handles[fd].fd = -1; // fd deaktivieren
}
}
bool inline IsPollMode(SHANDLE fd, int mode) // FileHandle auf Flag abfragen
{
if( ( poll_handles[fd].revents & mode ) == mode)
return true;
return false;
}
Das nächste war, die Funktionen auf Performance zu trimmen. Anfänglich hatte ich für jedes neue FD zuerst einen Schleifendurchlauf auf dem Array gemacht, um nach dem ersten Element zu suchen, dessen Feld 'fd' auf -1 steht, welches also frei war. Da habe ich dann das neue Filehandle eingehängt. Das war sehr zeitaufwändig. Dann habe ich das so gemacht, daß das Filehandle einfach an der Stelle im Array eingehängt wird, die seinem Wert entspricht. Das ist wesentlich performanter. Auch if-Abfragen gingen gehörig auf die Systemlast, weshalb ich sie auf eine reduziert habe.
fd_max ist ein Wert, der in einer anderen Funktion jeweils auf das bisher höchste genutzte Filehandle gesetzt wird. Dies war aber Funktionalität, die bei select() bereits vorhanden war. Ich konnte sie direkt übernehmen.
Achtung, einer der Fehler, die ich gemacht habe war, ein Flag mit dem Operator ^= löschen zu wollen. Da es sich dabei um ein Exklusiv-Oder handelt, war der Effekt immer der, daß das Flag zwar deaktiviert wurde, wenn es aktiviert war, jedoch wurde es auch immer aktiviert, wenn es zu dem Zeitpunkt deaktiviert war - was logischerweise sehr nervige Effekte nach sich zog. Der Einsatz des Operators &= mit dem bitweisen Komplement ~ des zu löschenden Flags erzielte den gewünschten Effekt.
Vielleicht helfen diese Ausführungen einigen, die mit den selben Problemen zu kämpfen haben. Tatsache ist, in einem Standard-Linux (LFS) ist select() hart begrenzt auf 1024 Filehandles. Dabei scheint es egal zu sein, auf welchen Wert man rlim_cur/rlim_max setzt und auch, auf welche maximale Größe man FD_Sets aufbohrt. Bei 1024 war bei mir Schluss.
Die Poll-Lösung ist genau so performant, jedoch nicht auf diese 1024 Handles begrenzt.
Danke allen für ihre Hilfe bei diesem Problem.
Viele Grüße,
Hendrik
Lesezeichen