2015-05-19 7 views
11

Lassen Sie uns an diesem Hallo Welt Programm einen BlickUnterschied zwischen FILE * "/ dev/stdout" und stdout

#include <stdio.h> 
int main(int argc, char ** argv) { 
    printf("Hello, World!"); 

    const char* sFile = "/dev/stdout"; // or /proc/self/fd/0 
    const char* sMode = "w"; 
    FILE * output = fopen(sFile, sMode); 
    //fflush(stdout) /* forces `correct` order */ 
    putc('!', output); // Use output or stdout from stdio.h 

    return 0; 
} 

Bei Verwendung des output Dateideskriptors kompiliert die Ausgabe lautet:

!Hello, World! 

bei der Kompilierung unter Verwendung des stdout Dateideskriptors, der von stdio.h bereitgestellt wird, ist die Ausgabe wie erwartet:

Hello, World!! 

I erläutert, wenn dieser mit dem letzteren putc ruft, wird es direkt um die stdout zu drucken, und wenn die Datei-Deskriptor auf /dev/stdout verwendet, wird es öffnet sich ein Rohr und in die drucken. Ich bin mir allerdings nicht sicher.

Das Verhalten ist noch interessanter, da es nicht das erste Zeichen des 'Hallo' überschreibt, sondern sich selbst in die erste Position des Zeilenpuffers vor der bereits gedrückten Zeichenfolge schiebt.

Aus logischer Sicht ist dies ruhig unerwartet.

Kann mir jemand erklären, was genau hier vor sich geht?


Ich cc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 und einen 3.13.0-52 Linux-Kernel w/gcc 4.8.2


bearbeiten zusammengestellt mit: I ein strace beider Programme gemacht haben, und hier ist der wichtige Teil:

die output (fopen ("/ dev/stdout", "w")) ohne fflush(stdout) scenar io erzeugt:

... 
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f62f21e9000 
write(3, "!", 1!)      = 1 
write(1, "Hello, World!", 13Hello, World!)   = 13 
exit_group(0)       = ? 

mit fflush(stdout) produziert und richtigen Reihenfolge erzwingt:

... 
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 
write(1, "Hello, World!", 13Hello, World!)   = 13 
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5ad4557000 
write(3, "!", 1!)      = 1 
exit_group(0)       = ? 

Die stdout (aus stdlib.h) Szenario erzeugt:

... 
write(1, "Hello, World!!", 14Hello, World!!)   = 14 
exit_group(0)       = ? 

So scheint es, die FILE * output = fopen("/dev/stdout") Stream verwendet einen anderen Dateideskriptor als stdout Auch, wie es scheint printf verwendet stdout So im dritten Szenario wird die Zeichenfolge zusammengebaut, bevor es auf den Strom geschoben wird.

+0

'stdout' ist eine Datei *, kein Dateideskriptor. Ähnlich ist "Ausgabe" kein Dateideskriptor. Jeder hat einen zugrunde liegenden Dateideskriptor, und wenn Sie direkt darauf schreiben würden, würden Sie dieses Verhalten nicht sehen. (Das direkte Schreiben in den Dateideskriptor umgeht die Pufferung.) –

+0

Der große Unterschied besteht darin, dass jede 'FILE *' einen eigenen Puffer verwendet, der nichts miteinander zu tun hat. –

Antwort

18

Beide Ströme (stdout und output) sind gepuffert. Nichts wird wirklich geschrieben, bis sie geleert sind. Da Sie sie nicht explizit löschen oder dafür sorgen, dass sie nicht automatisch geleert werden, werden sie nur automatisch geleert, wenn sie geschlossen werden.

Sie schließen sie auch nicht explizit, also werden sie von der Standardbibliothek on_exit Hooks geschlossen (und geleert). Und wie William Purpell richtig bemerkte, ist die Reihenfolge, in der die gepufferten I/O-Ströme geschlossen werden, nicht spezifiziert.

Sehen Sie fflush(3), fclose(3) und setbuf(3) Handbuchseiten für weitere Informationen zu steuern, wann und wie Ihre Ausgabe gelöscht wird.

+3

Ein wichtiger Punkt ist, dass die Reihenfolge, in der sie geschlossen werden, nicht spezifiziert ist . –

+0

Das Hinzufügen eines 'fflush (stdout)' vor dem 'putc (...)' im 'output'-Szenario ändert das Verhalten nicht. – MrPaulch

+1

Das macht es für mich ziemlich zuverlässig. Ich würde empfehlen, "strace" auf Ihrer resultierenden ausführbaren Datei auszuführen, um die Sequenz der Systemaufrufe zu sehen, die produziert werden. Dies kann jedoch recht aufschlussreich sein, um zu sehen, wie die gepufferten E/A-Funktionen mit dem System interagieren. –

2

/dev/stdout ist nicht dasselbe wie /proc/self/fd/0. In der Tat, wenn Sie nicht genug Privilegien haben, können Sie nicht schreiben /dev/stdout als /dev/stdout ist kein Standard-Zeichen-Gerät in Linux und jeder Versuch, fopen es mit der "w" Option wird versuchen, eine tatsächliche reguläre Datei auf diesem zu erstellen Verzeichnis. Die Zeichen Gerät Sie suchen, ist /dev/tty

In der Sprache C, stdout ist eine initialisierte globale Variable vom Typ FILE *, die auf die Standardausgabe-Datei verweist, das heißt, die Datei, deren Deskriptor 1. stdout existiert nur in der C Namespace und bezieht sich nicht auf eine tatsächliche Datei mit dem Namen "stdout"

+1

Ich weiß nicht, welche Distribution Sie verwenden, aber '/ dev/stdout' ist eine symbolische Verbindung zu'/proc/self/fd/1', die selbst eine symbolische Verbindung zu '/ dev/pts/XX' auf meiner ist . Sie sind also austauschbar. Am Ende war das nicht einmal das Problem :) – MrPaulch

+3

Es ist jedoch erwähnenswert, dass weder/dev/stdout,/proc/self/fd, oder/dev/fd standardisiert sind, und/dev/tty ist. – Random832