Wie werden E / A-Kanäle im Linux-Kernel implementiert?

8

stdin, stdout, stderr sind einige Ganzzahlen, die in eine Datenstruktur indizieren, die "weiß", welche E / A-Kanäle für den Prozess verwendet werden sollen. Ich verstehe, dass diese Datenstruktur für jeden Prozess einzigartig ist. Sind E / A-Kanäle nur einige Datenarray-Strukturen mit dynamischer Speicherzuordnung?

KawaiKx
quelle
Mit E / A-Kanälen meinen Sie Streams oder Pipes? Auch dies variiert wahrscheinlich je nach Kernel. Sie müssen wahrscheinlich nach einem bestimmten Kernel fragen.
strugee
1
@strugee Ich spreche über Linux-Kernel. Ich meinte Streams mit E / A-Kanälen. Wie werden diese Streams unter Linux implementiert? irgendein Array oder so?
KawaiKx

Antworten:

14

In Unix-artigen Betriebssystemen, die Standard - Eingabe, Ausgabe und Fehlerströme , die durch die Datei - Deskriptoren bezeichnet sind 0, 1, 2. Unter Linux sind diese unter dem procDateisystem in sichtbar /proc/[pid]/fs/{0,1,2}. Diese Dateien sind eigentlich symbolische Links zu einem Pseudoterminal- Gerät unter dem /dev/ptsVerzeichnis.

Ein Pseudoterminal (PTY) ist ein Paar virtueller Geräte, ein Pseudoterminal-Master (PTM) und ein Pseudoterminal-Slave (PTS) (zusammen als Pseudoterminal-Paar bezeichnet ), die einen IPC-Kanal bereitstellen, ähnlich einer bidirektionalen Pipe zwischen einem Programm, das dies erwartet verbunden mit einem Endgerät und einem Treiberprogramm, das das Pseudoterminal verwendet, um Eingaben an das vorherige Programm zu senden und Eingaben von diesem zu empfangen.

Ein wichtiger Punkt ist, dass der Pseudoterminal-Slave wie ein reguläres Terminal erscheint, z. B. kann er zwischen nicht- kanonischem und kanonischem Modus (Standardeinstellung) umgeschaltet werden , in dem er bestimmte Eingabezeichen interpretiert, z. B. das Erzeugen eines SIGINTSignals, wenn ein Interrupt- Zeichen (normalerweise generiert) durch Drücken von Ctrl+ Cauf der Tastatur) wird in den Pseudoterminal-Master geschrieben oder bewirkt, dass der nächste read()zurückkehrt, 0wenn ein Zeichen für das Dateiende (normalerweise durch Ctrl+ generiert D) auftritt. Andere von Terminals unterstützte Vorgänge sind das Ein- oder Ausschalten des Echos, das Festlegen der Prozessgruppe im Vordergrund usw.

Pseudoterminale haben eine Reihe von Verwendungsmöglichkeiten:

  • Sie ermöglichen es Programmen ssh, terminalorientierte Programme auf einem anderen Host zu betreiben, der über ein Netzwerk verbunden ist. Ein terminalorientiertes Programm kann ein beliebiges Programm sein, das normalerweise in einer interaktiven Terminalsitzung ausgeführt wird. Die Standardeingabe, -ausgabe und -fehler eines solchen Programms können nicht direkt an den Socket angeschlossen werden, da die Sockel die oben genannten terminalbezogenen Funktionen nicht unterstützen.

  • Sie ermöglichen es Programmen expect, ein interaktives terminalorientiertes Programm über ein Skript zu steuern.

  • Sie werden von Terminalemulatoren verwendet xterm, um terminalbezogene Funktionen bereitzustellen.

  • Sie werden von Programmen verwendet, um beispielsweise screenein einzelnes physisches Terminal zwischen mehreren Prozessen zu multiplexen.

  • Sie werden von Programmen verwendet script, um alle Ein- und Ausgaben aufzuzeichnen, die während einer Shell-Sitzung auftreten.

PTYs im Unix98-Stil , die unter Linux verwendet werden, werden wie folgt eingerichtet:

  • Das Treiberprogramm öffnet den Pseudo-Terminal-Master-Multiplexer dev/ptmx, an dem es einen Dateideskriptor für ein PTM empfängt, und ein PTS-Gerät wird im /dev/ptsVerzeichnis erstellt. Jeder durch Öffnen erhaltene Dateideskriptor /dev/ptmxist ein unabhängiges PTM mit einem eigenen zugeordneten PTS.

  • Die Treiberprogramme rufen fork()auf, um einen untergeordneten Prozess zu erstellen, der wiederum die folgenden Schritte ausführt:

    • Das Kind ruft setsid()an, um eine neue Sitzung zu starten, deren Sitzungsleiter das Kind ist. Dies führt auch dazu, dass das Kind sein Kontrollterminal verliert .

    • Das Kind öffnet das PTS-Gerät, das dem vom Treiberprogramm erstellten PTM entspricht. Da das Kind ein Sitzungsleiter ist, aber kein steuerndes Terminal hat, wird das PTS zum steuernden Terminal des Kindes.

    • Das Kind verwendet dup(), um den Dateideskriptor für das Slave-Gerät auf der Standardeingabe, -ausgabe und dem Fehler zu duplizieren.

    • Zuletzt ruft das Kind exec()auf, um das terminalorientierte Programm zu starten, das mit dem Pseudoterminalgerät verbunden werden soll.

Zu diesem Zeitpunkt wird alles, was das Treiberprogramm in das PTM schreibt, als Eingabe für das terminalorientierte Programm auf dem PTS angezeigt und umgekehrt.

Im kanonischen Modus wird der Eingang zum PTS zeilenweise gepuffert. Mit anderen Worten, genau wie bei regulären Terminals erhält das von einem PTS gelesene Programm nur dann eine Eingabezeile, wenn ein Zeilenumbruchzeichen in das PTM geschrieben wird. Wenn die Pufferkapazität erschöpft ist, write()blockieren weitere Aufrufe, bis ein Teil der Eingabe verbraucht ist.

Im Linux - Kernel, die Datei im Zusammenhang Systemaufrufe open(), read(), write() stat()etc. sind in der virtuellen Dateisystem (VFS) Schicht implementiert, die für Userspace - Programme einen einheitlichen Dateisystem - Schnittstelle zur Verfügung stellt. Das VFS ermöglicht die Koexistenz verschiedener Dateisystemimplementierungen innerhalb des Kernels. Wenn Userspace-Programme die oben genannten Systemaufrufe aufrufen, leitet das VFS den Aufruf an die entsprechende Dateisystemimplementierung weiter.

Die PTS-Geräte unter /dev/ptswerden von der in devptsdefinierten Dateisystemimplementierung verwaltet /fs/devpts/inode.c, während der TTY-Treiber, der das ptmxGerät im Unix98-Stil bereitstellt , in definiert ist drivers/tty/pty.c.

Das Puffern zwischen TTY-Geräten und TTY- Leitungsdisziplinen , wie z. B. Pseudoterminals, wird mit einer Pufferstruktur bereitgestellt, die für jedes in definierte tty-Gerät verwaltet wirdinclude/linux/tty.h

Vor Kernel Version 3.7 war der Puffer ein Flip-Puffer :

#define TTY_FLIPBUF_SIZE 512

struct tty_flip_buffer {
        struct tq_struct tqueue;
        struct semaphore pty_sem;
        char             *char_buf_ptr;
        unsigned char    *flag_buf_ptr;
        int              count;
        int              buf_num;
        unsigned char    char_buf[2*TTY_FLIPBUF_SIZE];
        char             flag_buf[2*TTY_FLIPBUF_SIZE];
        unsigned char    slop[4];
};

Die Struktur enthielt Speicher, der in zwei gleich große Puffer unterteilt war. Die Puffer wurden nummeriert 0(erste Hälfte char_buf/flag_buf) und 1(zweite Hälfte). Der Treiber hat Daten in dem durch gespeicherten Puffer gespeichert buf_num. Der andere Puffer könnte in die Leitungsdisziplin gespült werden.

Der Puffer wurde durch Umschalten buf_numzwischen 0und "umgedreht" 1. Bei buf_numÄnderung wurde char_buf_ptrund flag_buf_ptr auf den Anfang des durch buf_numund identifizierten Puffers gesetzt und countauf gesetzt 0.

Seit der Kernel-Version 3.7 wurden die TTY-Flip-Puffer durch Objekte ersetzt, die über kmalloc()in Ringen organisierte Objekte zugewiesen wurden . In einer normalen Situation für eine IRQ-gesteuerte serielle Schnittstelle bei typischen Geschwindigkeiten ist ihr Verhalten ziemlich das gleiche wie beim alten Flip-Buffer. Am Ende werden zwei Puffer zugewiesen, und der Kernel wechselt wie zuvor zwischen ihnen. Wenn es jedoch zu Verzögerungen kommt oder die Geschwindigkeit zunimmt, ist die Leistung der neuen Pufferimplementierung besser, da der Pufferpool etwas wachsen kann.

Thomas Nyman
quelle
So sachkundige Antworten sind äußerst schwer zu bekommen.
-Kohomologie
-1

Auf den Manpages für eine der drei wird die Antwort erklärt:

   Under  normal circumstances every UNIX program has three streams opened
   for it when it starts up, one for input, one for output,  and  one  for
   printing diagnostic or error messages.  These are typically attached to
   the user's terminal but might instead  refer  to  files  or
   other  devices,  depending  on what the parent process chose to set up.

   The input stream is referred to as "standard input"; the output  stream
   is  referred  to as "standard output"; and the error stream is referred
   to as "standard error".  These terms are abbreviated to form  the  sym-
   bols used to refer to these files, namely stdin, stdout, and stderr.

   Each  of these symbols is a stdio(3) macro of type pointer to FILE, and
   can be used with functions like fprintf(3) or fread(3).

   Since FILEs are a buffering wrapper around UNIX file  descriptors,  the
   same  underlying  files  may  also  be accessed using the raw UNIX file
   interface, that is, the functions like read(2) and lseek(2).

   On program startup, the integer file descriptors  associated  with  the
   streams  stdin,  stdout, and stderr are 0, 1, and 2, respectively.  The
   preprocessor symbols STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO are
   defined  with  these values in <unistd.h>.
Jeight
quelle
Diese Antwort beschreibt die Umsetzung stdin, stdoutund stderraus der Sicht der C - Bibliothek, aber die Frage ist explizit über die Kernel - Implementierung. Ich habe versucht, den Kernel-Standpunkt in meiner Antwort zu berücksichtigen .
Thomas Nyman