2016-04-11 10 views
0

Wenn ich sage, stdin, ich zum Strom beziehe mich durch fd bezeichnet = 0.Wird stdin unter Linux als Zeichengerät behandelt?

ich ein OS Kurs nehme die Geräte Block und Zeichen abdeckt. Es wurde ausdrücklich gesagt, dass die Tastatur ein Zeichengerät ist. Als uns jedoch der Systemaufruf read angezeigt wurde, wurde uns gesagt, dass es dem Kernel egal ist, von was er liest, solange es ein Blockgerät oder eine Datei auf einem Blockgerät ist.

#include <stdlib.h> 
#include <unistd.h> 

const int BUFFSIZE = 5; 

int main() { 
    int fd, n; 
    char buffer[BUFFSIZE]; 

    int stdin = 0; 
    int stdout = 1; 
    int stderr = 2; 

    do { 
    n = read (0, buffer, BUFFSIZE); 
    if (n < 0) { 
     write (stderr, "Error occurred\n", 10); 
    } else { 
     write (stdout, "Entered if\n", 20); 
     write (stdout, buffer, n); 
    } 
    } while (n > 0); 
    return 0; 
} 

Meine Frage ist:

Dies ist der Code, waren wir da, wie sich Linux behandeln Standardeingabe (fd = 0)? Wird es als Zeichengerät behandelt, oder tut der Kernel eine Art Pufferung? (Dies scheint nach den Ergebnissen beim Ausführen des Codes zu liegen.)

Zusätzlich wäre es nützlich zu wissen, ob ich den Code verwenden kann Der Lese-Syscall zum Lesen von Zeichengeräten im Allgemeinen. Wenn ja, ist die Eingabe gepuffert?

+6

'stdin' ist ein * stream *, kein * Gerät *. –

+1

_ "dem Kernel ist es egal, von was er liest, solange es ein Block-Gerät oder eine Datei auf einem Block-Gerät ist." _ Das ist nicht ganz richtig; Es gibt verschiedene Arten von Dateideskriptoren (Sockets, unbenannte Pipes, 'signalfd's,' eventfd's, 'epoll' Deskriptoren usw.), die keine Datei irgendwo im Dateisystem haben. Entscheidend ist, dass Sie einen Dateideskriptor haben und das Lesen unterstützt. –

+0

@PaulR Danke! Bearbeitet die Frage :) –

Antwort

4

Der Kernel tut normalerweise wenig oder gar keine Pufferung auf Zeichengeräten.

Der Kernel macht beim Lesen von Dateien in Dateisystemen eine gewisse Pufferung.

Sie können nicht sagen, welche Art von Gerät Standardeingabe ist, da es von Prozess zu Prozess variiert. Standardmäßig ist fd 0 normalerweise die Tastatur des Benutzers, bei der es sich um ein Zeichengerät handelt. Aber wenn ich sage

program < file 

dann ist fd 0 eine gewöhnliche Datei. Wenn ich sage

dann ist fd 0 ein Blockgerät. Und wenn ich daran arbeiten würde, könnte ich wahrscheinlich auch fd 0 an einen Netzwerkanschluss anschließen.

In Linux gibt es auch /proc/pid/fd/0, aber das ist auch kein Gerät; es sieht aus wie ein Symlink zu dem tatsächlichen Gerät in /dev, was auch immer es ist.


Nachtrag: ob ein bestimmtes Gerät gepuffert wird oder nicht hängt wirklich davon ab, wie der Treiber für das Gerät geschrieben. Jeder gegebene Treiber kann irgendeine Form der Pufferung implementieren oder nicht. Ob die Pufferung tatsächlich verwendet wird, hängt möglicherweise auch von anderen Faktoren ab.(Zum Beispiel sind die Unix-Terminal-Treiber alle standardmäßig gepuffert, aber diese Pufferung ist ausgeschaltet, wenn Sie den Treiber in den "cbreak" oder "raw" Modus setzen). Ich glaube nicht, dass Sie allgemeine Aussagen machen können, dass Zeichen- oder Blockgeräte gepuffert sind oder nicht.


Nachtrag 2: Wenn Sie anfangen, die Schichten abzuschälen, kann es ziemlich kompliziert werden. Unix bemüht sich mächtig (und macht in der Regel einen sehr guten Job), das richtige Gleichgewicht zwischen "Do-Was-Ich-Mittel" gegen "Keep-It-Simple" zu finden. Zum Beispiel, wenn Sie ein Terminal haben, das ist nicht Linie-gepuffert, und Sie fragen nach 10 Zeichen, aber es gibt nur 3 verfügbar, read() wird zurückgeben 3. Was ist das Richtige, aber es deutet darauf hin, dass es immer noch ein Puffer irgendwo, wo diese drei Zeichen zwischen der Zeit, die sie eingegeben wurden und der Zeit, die Sie sie gelesen haben. Außerdem, wenn Sie nur 3, aber 10 verfügbar waren, denke ich, dass unter anderen Umständen die anderen 7 für Sie gespeichert werden, was wiederum eine ziemlich große Menge an Kernel-Level-Pufferung nahelegt.

Aber im Rohmodus bin ich ziemlich sicher, dass Sie Zeichen verlieren können, wenn Sie sie nicht schnell genug lesen. Ich habe meine Aufmerksamkeit vom Terminaltreiber auf Netzwerk-Sockets gerichtet und dachte, dass unter bestimmten Umständen, wenn Sie einen read() auf einem UDP-Modus-Socket machen und das tatsächliche UDP-Paket größer als Ihre Leseanforderung ist, Sie den Rest des Pakets verlieren können da auch. [Obwohl ein Kommentator suggeriert, dass ich falsch liegen könnte.] (TCP-Modus-Buchsen, auf der anderen Seite, sind offensichtlich sehr stark gepuffert!)

Also, unter dem Strich: die Regeln können kompliziert sein, und die genauen Details hängen definitiv nicht ab nur der bestimmte Gerätetreiber im Einsatz, aber möglicherweise auch unzählige andere Details.

+0

Vielen Dank für Ihre Antwort! Ich habe immer noch etwas, über das ich nicht besonders klar bin.Angenommen, ich lese von einem Zeichengerät wie der Benutzer-Tastatur etwas aus, das länger als 1 Zeichen ist, sagt "ABC". Dann werde ich den Puffer mit der Zeichenfolge "ABC" haben, alle auf einmal zurückgegeben werden. Bedeutet dies nicht, dass der Kernel den Puffer verwendet, der zum Speichern der Ergebnisse (und somit zum Puffern) vorgesehen ist, und erst nach dem Eingeben eines Sonderzeichens wie '\ 0' oder '\ n' aus dem Syscall zurückkehrt die angegebene Grenze ist erreicht? –

+0

Ich habe meine Antwort gerade aktualisiert, als Sie diesen Kommentar gepostet haben. Ja, wie Jonathan Leffler uns erinnerte, hält der Kernel für "tty" -Geräte (Fernschreiber, d. H. Tastatur) im Allgemeinen einen Zeilenpuffer, so dass ja die Zeichengeräteeingabe gepuffert werden kann. –

+0

Wenn Sie über 'read()' 3 Zeichen anfordern, hängt das Verhalten immer noch vom Gerät ab. Wenn zum Beispiel bei einem Terminal oder einer Pipe ein "X" und ein Zeilenumbruch in der Eingabe vorhanden sind, werden diese 2 Zeichen zurückgegeben (und es gibt keinen Null-Terminator oder irgendetwas). Wenn in der Eingabe nichts enthalten ist, wird auf die Anzeige von Daten gewartet. Wenn 20 Zeichen in der Eingabe sind, wird 3 gelesen, während die anderen 17 für einen späteren Aufruf übrig bleiben. Das Mischen von Dateistrom-E/A (z. B. 'getchar()') mit direkten 'read()' Aufrufen des Dateideskriptors 0 wird zu großer Verwirrung führen. –

1

Es gibt keine echte Stdin in Unix. Die C-Laufzeitbibliothek definiert eine Symbolstdin, die dem ersten (0.) Dateideskriptor für den Prozess zugeordnet ist.

Per Konvention erstellen Unix-Shells drei Dateien, wenn sie einen Prozess erstellen. Konventionell werden sie auch als stdin, stdout und stderr bezeichnet.

Es ist nicht erforderlich, dass ein Unix-Prozess diese drei Dateien enthält. Es ist durchaus möglich, dass Sie eine eigene Shell erstellen, die wiederum Prozesse erstellt, ohne dass die Dateien 0, 1 oder 2 geöffnet sind.

Das Verhalten von stdin hängt davon ab, welcher Art von "Datei" (Datenstrom) es zugeordnet ist. Stdin könnte einer Tastatur zugeordnet sein oder einer Datei zugeordnet sein. In beiden Fällen können Sie Daten lesen. Nur in letzterem könntest du einen fseek machen.

+1

Traditionell war 'stdin' ein kompilierbares Makro erweitert zu '__iob [0]' (ein oder zwei Unterstriche - ich vergesse was) und es gab kein Symbol 'stdin' in den Bibliotheken. Es war ein ziemlicher Schock, als GNU C vor einem Jahrzehnt die Regeln änderte. Beide Systeme waren mit dem Standard konform; Der Standard lässt viel Spielraum für Implementierungen. –

Verwandte Themen