Eingabe-und Ausgabeverarbeitung

Dieser Abschnitt enthält eine Einführung in die Programmieraspekte für die Ein-und Ausgabeverarbeitung und die Subroutinen für die Ein-und Ausgabeverarbeitung.

Die Subroutinen der E/A-Bibliothek können Daten an oder von Einheiten oder Dateien senden. Das System behandelt Einheiten wie E/A-Dateien. Sie müssen beispielsweise auch ein Gerät wie eine Datei öffnen und schließen.

Einige Unterroutinen verwenden Standardeingabe und Standardausgabe als E/A-Kanäle. Für die meisten Unterroutinen können Sie jedoch eine andere Datei als Quelle oder Ziel der Datenübertragung angeben. Bei einigen Subroutinen können Sie einen Dateizeiger auf eine Struktur verwenden, die den Namen der Datei enthält. Bei anderen Subroutinen können Sie einen Dateideskriptor verwenden (d. h. die positive ganze Zahl, die der Datei beim Öffnen zugeordnet wird).

Die in der C-Bibliothek (libc.a) gespeicherten E/A-Subroutinen stellen Datenstrom-E/A bereit. Für den Zugriff auf diese Datenstrom-E/A-Subroutinen müssen Sie die Datei stdio.h mit der folgenden Anweisung einschließen:
#include <stdio.h>

Einige der E/A-Bibliothekssubroutinen sind Makros, die in einer Headerdatei definiert sind, und einige sind Objektmodule von Funktionen. In vielen Fällen enthält die Bibliothek ein Makro und eine Funktion, die denselben Operationstyp ausführen. Beachten Sie Folgendes, wenn Sie entscheiden, ob das Makro oder die Funktion verwendet werden soll:

  • Mit dem Programm dbx können Sie keinen Unterbrechungspunkt für ein Makro definieren.
  • Makros sind normalerweise schneller als ihre äquivalenten Funktionen, da der Vorprozessor die Makros durch tatsächliche Codezeilen im Programm ersetzt.
  • Makros führen nach der Kompilierung zu einem größeren Objektcode.
  • Funktionen können zu vermeiden Nebeneffekte haben.

Die bei der E/A-Verarbeitung verwendeten Dateien, Befehle und Subroutinen stellen die folgenden Schnittstellen bereit:

Untergeordnete Ebene
Die Low-Level-Schnittstelle bietet grundlegende Open-und Close-Funktionen für Dateien und Geräte.
Datenstrom
Die Datenstromschnittstelle stellt Lese-und Schreib-E/A für Pipes und FIFOs bereit.
Datenstation
Die Terminalschnittstelle stellt formatierte Ausgabe und Pufferung bereit.
Asynchron
Die asynchrone Schnittstelle ermöglicht gleichzeitige Ein-/Ausgabe und Verarbeitung.
Eingabesprache
Die Eingabesprache verwendet die Befehle lex und yacc , um ein lexikalisches Analyseprogramm und ein Parserprogramm für die Interpretation der Ein-/Ausgabe zu generieren.

Low-Level-E/A-Schnittstellen

Low-Level-E/A-Schnittstellen sind direkte Einstiegspunkte in einen Kernel, die Funktionen wie das Öffnen von Dateien, das Lesen und Schreiben von Dateien und das Schließen von Dateien bereitstellen.

Der Befehl line stellt die Schnittstelle bereit, mit der eine Zeile aus der Standardeingabe gelesen werden kann, und die folgenden Subroutinen stellen weitere Low-Level-E/A-Funktionen bereit:

open, openx oder creat
Bereiten Sie eine Datei oder ein anderes Pfadobjekt zum Lesen und Schreiben mithilfe eines zugeordneten Dateideskriptors vor.
read, readx, readv oder readvx
Aus einem offenen Dateideskriptor lesen
write, writex, writev oder writevx
In einen offenen Dateideskriptor schreiben
Schließen
Dateideskriptor aufgeben

Die Subroutinen open und creat richten Einträge in drei Systemtabellen ein. Ein Dateideskriptor indexiert die erste Tabelle, die als Datenbereich pro Prozess fungiert, auf den von Lese-und Schreibsubroutinen zugegriffen werden kann. Jeder Eintrag in dieser Tabelle hat einen Zeiger auf einen entsprechenden Eintrag in der zweiten Tabelle.

Die zweite Tabelle ist eine systemspezifische Datenbank oder Dateitabelle, die es ermöglicht, eine offene Datei von mehreren Prozessen gemeinsam zu nutzen. Die Einträge in dieser Tabelle geben an, ob die Datei zum Lesen, Schreiben oder als Pipe geöffnet war und wann sie geschlossen wurde. Es gibt auch einen Offset, um anzugeben, wo der nächste Lese-oder Schreibvorgang stattfinden wird, und einen endgültigen Zeiger auf den Eintrag in der dritten Tabelle, die eine Kopie des I-Node der Datei enthält.

Die Dateitabelle enthält Einträge für jede Instanz einer Subroutine open oder create für die Datei, aber die I-Node-Tabelle enthält nur einen Eintrag für jede Datei.

Hinweis: Während der Verarbeitung einer Subroutine open oder creat für eine Gerätedatei ruft das System immer die Subroutine open der Einheit auf, um eine spezielle Verarbeitung zu ermöglichen (z. B. das Zurückspulen eines Bands oder das Einschalten eines für Datenterminals bereiten Modemanschlusses). Das System verwendet die Subroutine close jedoch erst, wenn der letzte Prozess die Datei schließt (d. h., wenn der I-Node-Tabelleneintrag freigegeben wird). Dies bedeutet, dass ein Gerät die Anzahl seiner Benutzer nicht verwalten oder davon abhängen kann, es sei denn, ein Gerät mit exklusiver Nutzung (das verhindert, dass ein Gerät erneut geöffnet wird, bevor es geschlossen wird) wird implementiert.

Wenn eine Lese-oder Schreiboperation auftritt, werden die Argumente des Benutzers und der Dateitabelleneintrag verwendet, um die folgenden Variablen festzulegen:

  • Benutzeradresse des E/A-Zielbereichs
  • Bytezähler für die Übertragung
  • Aktuelle Position in der Datei

Wenn es sich bei der Datei, auf die verwiesen wird, um eine zeichenorientierte Gerätedatei handelt, wird die entsprechende Lese-oder Schreibsubroutine aufgerufen, um Daten zu übertragen und die Anzahl und die aktuelle Position zu aktualisieren. Andernfalls wird die aktuelle Position verwendet, um eine logische Blocknummer in der Datei zu berechnen.

Handelt es sich bei der Datei um eine normale Datei, muss die logische Blocknummer einer physischen Blocknummer zugeordnet werden. Eine blockorientierte Gerätedatei muss nicht zugeordnet werden. Die resultierende Nummer des physischen Blocks wird zum Lesen oder Schreiben der entsprechenden Einheit verwendet.

Blockeinheitentreiber können die Möglichkeit bieten, Informationen direkt zwischen dem Kernimage des Benutzers und dem Gerät in Blockgrößen zu übertragen, die so groß sind wie die Anforderungen des Anrufers, ohne Puffer zu verwenden. Die Methode umfasst die Einrichtung einer Zeichendatei, die der Roheinheit entspricht, und die Bereitstellung von Lese-und Schreibsubroutinen zum Erstellen eines privaten, nicht gemeinsam genutzten Pufferheaders mit den entsprechenden Informationen. Separate Subroutinen zum Öffnen und Schließen können bereitgestellt werden, und eine Subroutine mit Sonderfunktion kann für Magnetband aufgerufen werden.

Datenstrom-E/A-Schnittstellen

Datenstrom-E/A-Schnittstellen stellen Daten als Bytestrom bereit, der vom System nicht interpretiert wird. Dies bietet eine effizientere Implementierung für Netzprotokolle als die Zeichen-E/A-Verarbeitung. Beim Lesen und Schreiben mit Datenstrom-E/A gibt es keine Satzgrenzen. Beispielsweise kann ein Prozess, der 100 Byte aus einer Pipe liest, nicht feststellen, ob der Prozess, der die Daten in die Pipe geschrieben hat, einen einzigen Schreibvorgang von 100 Byte oder zwei Schreibvorgänge von 50 Byte ausgeführt hat oder ob die 100 Byte von zwei verschiedenen Prozessen stammen.

Datenstromein-/-ausgaben können Pipes oder FIFOs (First In/First Out-Dateien) sein. FIFOs sind Pipes ähnlich, da sie zulassen, dass die Daten nur eine Richtung (von links nach rechts) fließen. Eine FIFO-Warteschlange kann jedoch einen Namen erhalten und von nicht zugehörigen Prozessen aufgerufen werden, im Gegensatz zu einer Pipe. FIFOs werden manchmal als benannte Pipesbezeichnet. Da sie einen Namen hat, kann eine FIFO-Routine mit der Standard-E/A-Subroutine fopen geöffnet werden. Zum Öffnen einer Pipe müssen Sie die Subroutine pipe aufrufen, die einen Dateideskriptor zurückgibt, und die Standard-E/A-Subroutine fdopen aufrufen, um einen offenen Dateideskriptor einem Standard-E/A-Datenstrom zuzuordnen.

Anmerkung: Datenstrom-E/A-Schnittstellen puffern Daten auf Benutzerebene und können die Daten erst schreiben, wenn die Subroutine fclose oder fflush ausgeführt wird. Dies kann zu unerwarteten Ergebnissen führen, wenn sie mit Datei-E/A wie read () oder write () gemischt werden.

Der Zugriff auf Datenstrom-E/A-Schnittstellen erfolgt über die folgenden Subroutinen und Makros:

fclose
Schließt einen Datenstrom
feof, ferror, clearerr oder fileno
Status eines Datenstroms überprüfen
fflush
Alle derzeit gepufferten Zeichen aus einem Stream schreiben
fopen, freopen oder fdopen
Stream öffnen
fread oder fwrite
Binäre Eingabe ausführen
fseek, rewind, ftell, fgetpos oder fsetpos
Dateizeiger eines Datenstroms neu positionieren
getc, fgetc, getchar oder getw
Zeichen oder Wort aus einem Eingabedatenstrom abrufen
gets oder fgets
Zeichenfolge aus einem Stream abrufen
getwc, fgetwc oder getwchar
Ein Breitzeichen aus einem Eingabedatenstrom abrufen
getws oder fgetws
Zeichenfolge aus einem Stream abrufen
printf, fprintf, sprintf, wsprintf, vprintf, vfprintf, vsprintf oder vwsprintf
Formatierte Ausgabe drucken
putc, putchar, fputc oder putw
Ein Zeichen oder ein Wort in einen Datenstrom schreiben
puts oder fputs
Zeichenfolge in einen Datenstrom schreiben
putwc, putwchar oder fputwc
Ein Zeichen oder ein Wort in einen Datenstrom schreiben
putws oder fputws
Breite Zeichenfolge in einen Datenstrom schreiben
scanf, fscanf, sscanf oder wsscanf
Formatierte Eingabe konvertieren
setbuf, setvbuf, setbuffer oder setlinebuf
Pufferung einem Datenstrom zuordnen
ungetc oder ungetwc
Zeichen zurück in den Eingabedatenstrom schieben

E/A-Schnittstellen für Terminals

Terminal-E/A-Schnittstellen arbeiten zwischen einem Prozess und dem Kernel und stellen Funktionen wie Pufferung und formatierte Ausgabe bereit. Jedes Terminal und Pseudoterminal hat eine TTY-Struktur, die die aktuelle Prozessgruppen-ID enthält. Dieses Feld gibt die Prozessgruppe an, die die dem Terminal zugeordneten Signale empfangen soll. Auf E/A-Terminalschnittstellen kann über den Befehl iostat zugegriffen werden, der das Laden von E/A-Systemeinheiten überwacht, und über den Dämon uprintfd , der das Schreiben von Kernelnachrichten in die Systemkonsole ermöglicht.

Terminalmerkmale können über die folgenden Subroutinen aktiviert oder inaktiviert werden:

cfgetospeed, cfsetospeed, cfgetispeedoder cfsetispeed
Eingabe-und Ausgabebaudraten abrufen und festlegen
ioctl
Führt Steuerfunktionen aus, die dem Terminal zugeordnet sind
Terminaldefinition
Abfragenterminalmerkmale
tcdrain
Wartet auf Abschluss der Ausgabe
tcflow
Führt Ablaufsteuerungsfunktionen aus
tcflush
Löscht Daten aus der angegebenen Warteschlange
tcgetpgrp
Ruft die ID der Vordergrundprozessgruppe ab
tcsendbreak
Sendet einen Umbruch in einer asynchronen seriellen Datenleitung
tcset-attribut
Legt den Terminalstatus fest
ttylock, ttywait, ttyunlockoder ttylocked
TTY-Sperrfunktionen steuern
ttyname oder isatty
Namen eines Terminals abrufen
ttyslot
Sucht den Slot in der Datei utmp für den aktuellen Benutzer.

Asynchrone E/A-Schnittstellen

Asynchrone E/A-Subroutinen ermöglichen es einem Prozess, eine E/A-Operation zu starten und die Subroutine unmittelbar nach dem Start oder in der Warteschlange zurückzugeben. Eine weitere Subroutine muss warten, bis die Operation abgeschlossen ist (oder sofort zurückkehren, wenn die Operation bereits beendet ist). Dies bedeutet, dass ein Prozess seine Ausführung mit seiner Ein-/Ausgabe oder der Ein-/Ausgabe zwischen verschiedenen Einheiten überschneiden kann. Obwohl die asynchrone Ein-/Ausgabe die Leistung für einen Prozess, der von einer Plattendatei liest und in eine andere Plattendatei schreibt, nicht wesentlich verbessert, kann die asynchrone Ein-/Ausgabe erhebliche Leistungsverbesserungen für andere Typen von E/A-gesteuerten Programmen bieten, wie z. B. Programme, die eine Platte auf einem Magnetband ausgeben oder ein Bild auf einer Bildanzeige anzeigen.

Obwohl nicht erforderlich, kann ein Prozess, der asynchrone Ein-/Ausgabe ausführt, den Kernel anweisen, ihn zu benachrichtigen, wenn ein angegebener Deskriptor für Ein-/Ausgabe bereit ist (auch als signalgesteuerte Ein-/Ausgabebezeichnet). Bei Verwendung von LEGACY AIO benachrichtigt der Kernel den Benutzerprozess mit dem Signal SIGIO. Bei Verwendung von POSIX AIO wird die Struktur sigevent vom Programmierer verwendet, um zu bestimmen, welches Signal für den Kernel verwendet werden soll, um den Benutzerprozess zu benachrichtigen. Signale umfassen SIGIO, SIGUSR1und SIGUSR2.

Zur Verwendung der asynchronen Ein-/Ausgabe muss ein Prozess die folgenden Schritte ausführen:

  1. Erstellen Sie einen Handler für das Signal SIGIO . Dieser Schritt ist nur erforderlich, wenn eine Benachrichtigung durch das Signal angefordert wird.
  2. Legen Sie die Prozess-ID oder die Prozessgruppen-ID für den Empfang der SIGIO -Signale fest. Dieser Schritt ist nur erforderlich, wenn eine Benachrichtigung durch das Signal angefordert wird.
  3. Asynchrone Ein-/Ausgabe aktivieren. Der Systemadministrator bestimmt normalerweise, ob asynchrone Ein-/Ausgabe geladen (aktiviert) wird. Die Aktivierung erfolgt beim Systemstart.

Die folgenden asynchronen E/A-Subroutinen werden bereitgestellt:

aio_abbrechen
Bricht eine oder mehrere ausstehende asynchrone E/A-Anforderungen ab.
E/A-Fehler
Ruft den Fehlerstatus einer asynchronen E/A-Anforderung ab.
aio_fsync
Synchronisiert asynchrone Dateien.
aio_nwait
Setzt den aufrufenden Prozess aus, bis eine bestimmte Anzahl asynchroner E/A-Anforderungen abgeschlossen ist.
aio_lesen
Liest asynchron aus einem Dateideskriptor
aio_rückgabe
Ruft den Rückgabestatus einer asynchronen E/A-Anforderung ab.
aio_aussetzen
Setzt den aufrufenden Prozess aus, bis eine oder mehrere asynchrone E/A-Anforderungen abgeschlossen sind.
E/A-Schreibvorgänge
Schreibt asynchron in einen Dateideskriptor.
lio_listio
Leitet eine Liste asynchroner E/A-Anforderungen mit einem einzigen Aufruf ein.
poll oder select
E/A-Status mehrerer Dateideskriptoren und Nachrichtenwarteschlangen prüfen

Zur Verwendung mit der Subroutine poll werden die folgenden Headerdateien bereitgestellt:

poll.h
Definiert die Strukturen und Flags, die von der Subroutine poll verwendet werden.
aio.h
Definiert die Struktur und die Flags, die von den Subroutinen aio_read, aio_writeund aio_suspend verwendet werden