2015-05-19 22 views
11

Diamo uno sguardo a questo programma Ciao MondoDifferenza tra FILE * "/ dev/stdout" e 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; 
} 

Quando compilato utilizzando il descrittore output file l'output è:

!Hello, World! 

quando viene compilato utilizzando il descrittore di file stdout fornito da stdio.h l'output è come previsto:

Hello, World!! 

Immagino che quando si chiama putc con quest'ultimo, stamperà direttamente al stdout e quando si utilizza il descrittore di file su /dev/stdout si aprirà una pipe e stampare in quello. Non sono sicuro però.

Il comportamento è ancora più interessante, in quanto non sovrascrive il primo carattere di "Hello", ma si spinge piuttosto nella prima posizione del buffer di riga di fronte alla stringa già premuta.

Da un punto di vista logico questo è silenzioso inaspettato.

Qualcuno può spiegare cosa sta succedendo esattamente qui?


sto usando cc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 e un kernel linux 3.13.0-52 compilato w/gcc 4.8.2


Edit: Ho fatto un strace di entrambi i programmi, e qui è la parte importante:

il output (fopen ("/ dev/stdout", "w")) senza fflush(stdout) scenar io produce:

... 
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)       = ? 

utilizzando fflush(stdout) produce e impone ordine corretto:

... 
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)       = ? 

Il stdout (da stdlib.h) scenario produce:

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

così sembra la FILE * output = fopen("/dev/stdout") stream utilizza un descrittore di file diverso da stdout Anche come sembra che printf usi stdout Quindi nel terzo scenario la stringa viene assemblata prima di essere spinta nello stream.

+0

'stdout' è un FILE *, non un descrittore di file. Allo stesso modo, 'output' non è un descrittore di file. Ognuno ha un descrittore di file sottostante e se si dovesse scrivere direttamente ad esso non si vedrebbe questo comportamento. (Scrittura diretta sul descrittore di file ignora il buffering.) –

+0

La grande differenza è che ogni "FILE *" utilizza il proprio buffer, non correlato tra loro. –

risposta

18

Entrambi i flussi (stdout e output) sono memorizzati nel buffer. Nulla viene effettivamente scritto finché non vengono scaricati. Dal momento che non li stai svuotando esplicitamente, né disponendo che vengano automaticamente scaricati, vengono chiusi automaticamente quando vengono chiusi.

Inoltre, non vengono chiusi esplicitamente, quindi vengono chiusi (e scaricati) dagli hook della libreria standard on_exit. E come William Pursell ha giustamente sottolineato, l'ordine in cui i flussi I/O bufferizzati sono chiusi non è specificato.

Vedere le pagine di manuale fflush(3), fclose(3) e setbuf(3) per ulteriori informazioni sul controllo di quando e come viene scaricato l'output.

+3

Un punto chiave è che l'ordine in cui vengono chiusi non è specificato . –

+0

L'aggiunta di un 'fflush (stdout)' prima del 'putc (...)' nello scenario 'output' non cambia il comportamento. – MrPaulch

+1

Lo fa per me, abbastanza affidabile. Raccomanderei di eseguire 'strace' sul file eseguibile risultante per vedere la sequenza di chiamate di sistema che vengono prodotte. Questo può essere abbastanza illuminante in ogni caso per vedere come le funzioni di I/O bufferizzate interagiscono con il sistema. –

2

/dev/stdout non corrisponde a /proc/self/fd/0. In effetti, se non hai abbastanza privilegi, non sarai in grado di scrivere su /dev/stdout come /dev/stdout non è un dispositivo di carattere standard in Linux e qualsiasi tentativo di aprirlo con l'opzione "w" proverà a creare un file regolare effettivo su quello directory. Il dispositivo di carattere che si sta cercando è /dev/tty

Nel linguaggio C, stdout è una variabile globale di tipo inizializzata FILE *, che punta al file standard output, cioè, il file di cui descrittore è 1. stdout esiste solo nel C namespace e non si riferisce a nessun file attuale denominato "stdout"

+1

Non so quale distro stai usando ma '/ dev/stdout' è un collegamento simbolico a'/proc/self/fd/1' che a sua volta è un collegamento simbolico a '/ dev/pts/XX' sul mio . Quindi sono intercambiabili. Alla fine, non era nemmeno il problema :) – MrPaulch

+3

Tuttavia, vale la pena ricordare che né/dev/stdout,/proc/self/fd, o/dev/fd sono standardizzati, e/dev/tty è. – Random832