Heim > Artikel > Betrieb und Instandhaltung > Ausführliche Erläuterung von Beispielen zur IO-Pufferverwaltung
Der Schreibprototyp im Linux-System-IO ist ssize_t write(int filedes, const void * buff, size_t nbytes);
Beim Aufruf von write Beim Lesen von Daten kehrt der Schreibvorgang direkt nach Abschluss des Aufrufs zurück, die Festplatte ist jedoch ein langsames Gerät. Das Betriebssystem speichert die Daten im Puffer im Kernel und ist für das asynchrone Schreiben der Daten auf die Festplatte verantwortlich. Wenn das System zu diesem Zeitpunkt ausfällt, gehen natürlich Daten verloren. „Write“ ist ein Systemaufruf, und jeder Aufruf wird den Kernel abfangen. Daher kann die Auswahl einer geeigneten Blocklängen-Buffsize und die Minimierung der Aufrufe die Effizienz optimieren. Wenn wir in der Standard-IO von ANSI C printf/fprintf/fputs usw. aufrufen, werden sie in einem Stream verarbeitet. Wir müssen nur in den Stream schreiben, anstatt eine Puffergröße wie „Write“ auszuwählen, da dies von der Standard-IO-Bibliothek verarbeitet wird viele Details für uns, wie Pufferzuweisung, Durchführung von E/A mit optimierter Länge usw. Dadurch wird die Anzahl der Schreib-/Lese-Systemaufrufe reduziert und die Effizienz verbessert. Gleichzeitig wird jedoch ein weiteres Problem eingeführt: Beim Kopieren von Daten müssen beispielsweise bei Verwendung der Funktionen fgets und fputs normalerweise zwei Puffer durchlaufen werden: einer ist der Standard-E / A-Puffer und der andere ist der Kernel-Puffer Aufrufe lesen und schreiben. Aber im Allgemeinen hat die Verwendung von Standard-IO eine einfachere Schnittstelle als System-IO und ist ebenso effizient.
Standard-IO bietet drei Arten von Puffern: Vollcache, Zeilencache und kein Cache. Der vollständige Cache wird nur dann aktiv geleert, wenn der Puffer voll ist. Er wird normalerweise für A-Festplattendatei-IO verwendet . Der Zeilencache wird geleert, wenn er auf ein Zeilenumbruchzeichen im Puffer trifft. In einem anderen Fall wird der Puffer geleert, wenn Eingabedaten aus der Standardeingabe und -ausgabe abgerufen werden müssen. Der Zeilencache wird im Allgemeinen in interaktiven Terminals verwendet. Ohne Zwischenspeicherung entspricht dies dem direkten Schreiben der Systemaufrufausgabe. Der Standardfehlerstrom stderr wird normalerweise nicht zwischengespeichert, wodurch die Fehlermeldung so schnell wie möglich angezeigt wird. Zusätzlich zu den Standard-Flush-Bedingungen wird der Puffer auch geleert, wenn die Flush-Funktion explizit aufgerufen wird und das Programm normal beendet wird. Wir können setbuf/setvbuf verwenden, um die Standardpufferlänge zu ändern, siehe APUE Abschnitt 5.4.
Wenn wir in einem Programm, das Standard-E/A verwendet, eine Standardausgabe in eine Datei umleiten, wird der Zeilencache zu einem vollen Cache, was zu einigen unerwarteten Fehlern führen kann, z. B. beim Aufruf printf("*****n") wird normal ausgegeben, wenn das Programm im interaktiven Modus ausgeführt wird. Wenn die Standardausgabe jedoch in eine Datei umgeleitet wird, wird der Pufferbereich vollständig zwischengespeichert, printf wird nicht normal ausgegeben und die Datenzeile befindet sich immer noch im Puffer. Wenn Sie zu diesem Zeitpunkt einen untergeordneten Prozess verzweigen, werden beim Kopieren des Datenraums in den untergeordneten Prozess auch die Pufferdaten in den untergeordneten Prozess kopiert. Wenn dann die Ausgabe im untergeordneten Prozess erfolgt, wird der vorherige Inhalt im Puffer aktualisiert, was zu einer unerwarteten Ausgabe führt.
Bei der Netzwerkprogrammierung sollte System-IO direkt verwendet werden. Der durch Standard-IO eingeführte Pufferungsmechanismus zur Verbesserung der Leistung erhöht die Komplexität von Netzwerkanwendungen. Darüber hinaus ist der Standard-E/A-Stream gewissermaßen Vollduplex und kann gleichzeitig Eingaben und Ausgaben durchführen. Einschränkungen für Streams und Einschränkungen für Sockets stehen jedoch manchmal im Widerspruch zueinander. (Siehe CSAPP P611)
Einige erweiterte Netzwerkbibliotheken (wie die Muduo-Bibliothek) erstellen ihre eigenen Puffer basierend auf der Verwendung von System-E/A, um Benutzern dabei zu helfen, System-E/A abzuschirmen. Einige Unannehmlichkeiten, wie z Wenn zum Beispiel beim Aufrufen von „write“ eine große Datenmenge gesendet wird, muss die Anwendungsschicht warten, bis der Sendepuffer voll ist. Wenn „read“ Daten empfängt, bleiben Pakete hängen und die Daten werden langsam empfangen. Wenn der Anwendungsschichtpuffer hinzugefügt wird, verarbeitet die Netzwerkbibliothek diese Implementierungsdetails, um Benutzervorgänge zu vereinfachen.
Linux bietet auch Zero-Copy-Technologie, um Speicherkopien zu reduzieren und dadurch die Effizienz zu verbessern. Wir wissen, dass die Verwendung von Lese-/Schreibvorgängen zum Senden von Daten von der Festplatte an die Netzwerkkarte vier Kopiervorgänge durchläuft: wenn eine Anwendung dies benötigt Um auf ein bestimmtes Datenelement zuzugreifen, prüft der Betriebssystemkern zunächst, ob die Daten aufgrund eines vorherigen Zugriffs auf dieselbe Datei im Puffer des Betriebssystemkernel-Adressraums gespeichert wurden im Kernelpuffer, Linux Der Betriebssystemkernel liest diese Daten zunächst von der Festplatte und legt sie im Puffer des Betriebssystemkernels ab. Wenn dieser Datenlesevorgang durch DMA abgeschlossen wird, muss die CPU während des Datenlesevorgangs nur die Pufferverwaltung durchführen und DMA erstellen und verarbeiten. Darüber hinaus muss die CPU keine weiteren Änderungen vornehmen Nachdem DMA den Datenlesevorgang ausgeführt hat, benachrichtigt es das Betriebssystem zur weiteren Verarbeitung. Das Linux-Betriebssystem speichert diese Daten im Adressraum der Anwendung, die diese Daten angefordert hat, basierend auf der Adresse des Anwendungsadressraums, der durch den Lesesystemaufruf angegeben wurde. Nachdem der Benutzer den Vorgang für die Daten abgeschlossen hat Das Betriebssystem muss die Daten aus dem Puffer im Adressraum der Benutzeranwendung in den Kernel-Puffer des Netzwerkstapels wiederherstellen. Nachdem der Datenkopiervorgang abgeschlossen ist, werden die Daten gepackt und dann an die Netzwerkschnittstellenkarte gesendet. Wie aus der obigen Beschreibung hervorgeht, werden die Daten bei diesem herkömmlichen Datenübertragungsprozess mindestens viermal kopiert. Auch wenn DMA für die Kommunikation mit der Hardware verwendet wird, muss die CPU dennoch zweimal auf die Daten zugreifen.
(ps: Ich erinnere mich, dass ich zuvor eine Interviewfrage gelesen habe, in der stand, dass der Printf-Ausgabeprozess mehrere Puffer durchläuft, jetzt versteht es jeder!)
Durch die Verwendung der Zero-Copy-Technologie kann das Kopieren von Daten in den Puffer des Systemkernel-Adressraums und den Puffer des Adressraums der Benutzeranwendung vermieden werden. Manchmal muss die Anwendung während des Datenübertragungsprozesses nicht auf die Daten zugreifen. Die übertragenen Daten müssen nicht in den Benutzeranwendungsbereich kopiert werden, sondern können über den Kernel direkt an die Netzwerkkarte gesendet werden. und derzeit ist keine Kopie erforderlich. Unter Linux können Sie mmap, sendfile und splice verwenden, um eine Nullkopie zu erreichen.
Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung von Beispielen zur IO-Pufferverwaltung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!