Antworten:
Ich denke, dass dies Ihre Frage beantwortet:
Von Richard Stevens (rstevens@noao.edu):
Der grundlegende Unterschied besteht darin, dass fd_set von select () eine Bitmaske ist und daher eine feste Größe hat. Es wäre möglich, dass der Kernel diese Größe beim Kompilieren des Kernels nicht einschränkt, sodass die Anwendung FD_SETSIZE nach Belieben definieren kann (wie die Kommentare im Systemheader heute implizieren), aber es erfordert mehr Arbeit. Der Kernel von 4.4BSD und die Solaris-Bibliotheksfunktion haben beide diese Grenze. Ich sehe jedoch, dass BSD / OS 2.1 jetzt codiert wurde, um diese Grenze zu umgehen. Es ist also machbar, nur eine kleine Frage der Programmierung. :-) Jemand sollte einen Solaris-Fehlerbericht dazu einreichen und prüfen, ob er jemals behoben wird.
Bei poll () muss der Benutzer jedoch ein Array von pollfd-Strukturen zuweisen und die Anzahl der Einträge in diesem Array übergeben, sodass es keine grundlegende Begrenzung gibt. Wie Casper feststellt, haben weniger Systeme poll () als select, sodass letzteres portabler ist. Außerdem konnten Sie bei Originalimplementierungen (SVR3) den Deskriptor nicht auf -1 setzen, um den Kernel anzuweisen, einen Eintrag in der pollfd-Struktur zu ignorieren, was es schwierig machte, Einträge aus dem Array zu entfernen. SVR4 umgeht dies. Persönlich verwende ich immer select () und selten poll (), da ich meinen Code auch auf BSD-Umgebungen portiere. Jemand könnte eine Implementierung von poll () schreiben, die select () für diese Umgebungen verwendet, aber ich habe noch nie eine gesehen. Sowohl select () als auch poll () werden durch POSIX 1003.1g standardisiert.
Die oben genannte E-Mail ist mindestens so alt wie 2001; Der poll()
Befehl wird jetzt (2017) auf allen modernen Betriebssystemen unterstützt - einschließlich BSD. In der Tat glauben einige Leute, dass select()
dies veraltet sein sollte . Abgesehen von den Meinungen sind Portabilitätsprobleme poll()
bei modernen Systemen kein Problem mehr. Darüber hinaus wurde epoll()
seitdem entwickelt (Sie können die Manpage lesen ) und erfreut sich immer größerer Beliebtheit.
Für die moderne Entwicklung möchten Sie wahrscheinlich nicht verwenden select()
, obwohl daran nichts explizit falsch ist. poll()
und es ist eine modernere Evolution epoll()
, bieten die gleichen Funktionen (und mehr) wie select()
ohne unter den darin enthaltenen Einschränkungen zu leiden.
select
oder poll
:(
Mit dem select()
Aufruf müssen Sie drei Bitmasken erstellen, um zu markieren, welche Sockets und Dateideskriptoren auf Lesen, Schreiben und Fehler überwacht werden sollen. Anschließend markiert das Betriebssystem, welche tatsächlich eine Aktivität hatten. poll()
Haben Sie eine Liste von Deskriptor-IDs erstellt, und das Betriebssystem markiert jede von ihnen mit der Art des aufgetretenen Ereignisses.
Die select()
Methode ist ziemlich klobig und ineffizient.
In der Regel stehen einem Prozess mehr als tausend potenzielle Dateideskriptoren zur Verfügung. Wenn in einem lang laufenden Prozess nur wenige Deskriptoren offen sind, aber mindestens einem von ihnen eine hohe Nummer zugewiesen wurde, muss die übergebene Bitmaske select()
groß genug sein, um diesen höchsten Deskriptor aufzunehmen - also werden ganze Bereiche von Hunderten von Bits aufgenommen Stellen Sie sicher, dass das Betriebssystem bei jedem select()
Aufruf eine Schleife durchlaufen muss, um festzustellen, dass sie nicht festgelegt sind.
Nach der select()
Rückkehr muss der Anrufer alle drei Bitmasken durchlaufen, um festzustellen, welche Ereignisse stattgefunden haben. In sehr vielen typischen Anwendungen erhalten zu einem bestimmten Zeitpunkt nur ein oder zwei Dateideskriptoren neuen Datenverkehr. Alle drei Bitmasken müssen jedoch bis zum Ende gelesen werden, um festzustellen, um welche Deskriptoren es sich handelt.
Da das Betriebssystem Sie durch Umschreiben der Bitmasken über Aktivitäten informiert, sind diese ruiniert und nicht mehr mit der Liste der Dateideskriptoren gekennzeichnet, die Sie anhören möchten. Sie müssen entweder die gesamte Bitmaske aus einer anderen Liste neu erstellen, die Sie im Speicher behalten, oder Sie müssen memcpy()
nach jedem select()
Aufruf eine doppelte Kopie jeder Bitmaske und des Datenblocks über den zerstörten Bitmasken aufbewahren .
Der poll()
Ansatz funktioniert also viel besser, da Sie dieselbe Datenstruktur weiterhin verwenden können.
Tatsächlich poll()
hat ein weiterer Mechanismus in modernen Linux-Kerneln inspiriert: Er epoll()
verbessert den Mechanismus noch weiter, um einen weiteren Sprung in der Skalierbarkeit zu ermöglichen, da die heutigen Server häufig Zehntausende von Verbindungen gleichzeitig verarbeiten möchten. Dies ist eine gute Einführung in die Bemühungen:
http://scotdoyle.com/python-epoll-howto.html
Während dieser Link einige schöne Grafiken enthält, die die Vorteile von zeigen epoll()
(Sie werden feststellen, dass select()
dies zu diesem Zeitpunkt als so ineffizient und altmodisch angesehen wird, dass in diesen Grafiken nicht einmal eine Linie angezeigt wird!):
http://lse.sourceforge.net/epoll/index.html
Update: Hier ist eine weitere Frage zum Stapelüberlauf, deren Antwort noch detaillierter auf die Unterschiede eingeht:
Beide sind langsam und meistens gleich , aber unterschiedlich groß und in irgendeiner Form!
Wenn Sie einen Iterator schreiben, müssen Sie den Satz select
jedes Mal kopieren ! Während poll
hat diese Art von Problem behoben, um schönen Code zu haben. Ein weiterer Unterschied besteht darin, dass poll
standardmäßig mehr als 1024 Dateideskriptoren (FDs) verarbeitet werden können. poll
kann verschiedene Ereignisse behandeln, um das Programm besser lesbar zu machen, anstatt viele Variablen für diese Art von Arbeit zu haben. Operationen in poll
und select
sind linear und langsam, da viele Überprüfungen durchgeführt werden.