2015-07-08 10 views
20

Stavo eseguendo un programma (valgrind, in realtà) sulla mia macchina Ubuntu, e avevo reindirizzato sia stdout che stderr su file diversi. Sono stato sorpreso di vedere sullo schermo un breve messaggio: com'è possibile? Come potrei farlo da solo in un programma C++?Come può un programma Unix visualizzare sullo schermo anche quando lo stdout e lo stderr vengono reindirizzati?

EDIT: Ecco il comando che ho usato, e l'uscita:

$ valgrind ./myprogram > val.out 2> val.err 
*** stack smashing detected ***: ./myprogram terminated 

EDIT2: Giocando con un po 'di più, si scopre che myprogram, non valgrind, sta causando il messaggio da stampare, e come risposta al di sotto, sembra che gcc pila smashing codice di rilevamento è la stampa a/dev/tty

+1

Puoi condividere il vostro comando utilizzato – Steephen

+2

aprire direttamente il dispositivo di visualizzazione. per esempio. 'echo 'foo'>/dev/pts/0' –

+0

Sembra un messaggio dato dal kernel – xuhdev

risposta

16

non è scritto da valgrind, ma piuttosto glibc e il vostro ./myprogram sta usando glibc:

#define _PATH_TTY "/dev/tty" 

/* Open a descriptor for /dev/tty unless the user explicitly 
    requests errors on standard error. */ 
const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_"); 
if (on_2 == NULL || *on_2 == '\0') 
    fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY); 

if (fd == -1) 
    fd = STDERR_FILENO; 

... 
written = WRITEV_FOR_FATAL (fd, iov, nlist, total); 

Qui di seguito sono alcune parti rilevanti della glibc:

void 
__attribute__ ((noreturn)) 
__stack_chk_fail (void) 
{ 
    __fortify_fail ("stack smashing detected"); 
} 

void 
__attribute__ ((noreturn)) 
__fortify_fail (msg) 
    const char *msg; 
{ 
    /* The loop is added only to keep gcc happy. */ 
    while (1) 
    __libc_message (2, "*** %s ***: %s terminated\n", 
      msg, __libc_argv[0] ?: "<unknown>"); 
} 


/* Abort with an error message. */ 
void 
__libc_message (int do_abort, const char *fmt, ...) 
{ 
    va_list ap; 
    int fd = -1; 

    va_start (ap, fmt); 

#ifdef FATAL_PREPARE 
    FATAL_PREPARE; 
#endif 

    /* Open a descriptor for /dev/tty unless the user explicitly 
    requests errors on standard error. */ 
    const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_"); 
    if (on_2 == NULL || *on_2 == '\0') 
    fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY); 

    if (fd == -1) 
    fd = STDERR_FILENO; 

    ... 
    written = WRITEV_FOR_FATAL (fd, iov, nlist, total); 
+0

Questo glibc './myprogram' sta facendo la stampa? E non eredita lo stderr/stdout di 'valgrind'? – Yakk

+3

Si potrebbe voler aggiungere che è la directory scritta nella console aprendo '_PATH_TTY' che è' "/ dev/tty" 'sulla maggior parte dei sistemi, perché questa è la domanda vera e propria. – dhke

+2

@Yakk: Sì, 'myprogram' usa glibc, quindi la routine viene chiamata nel contesto del processo'./Mioprogramma'. Sì, eredita lo stdout e lo stderr di 'valgrind' - ma non scrive su stdout o stderr. Sembra che apra esplicitamente '_PATH_TTY', che probabilmente è qualcosa come'/dev/tty'. In generale, un programma può scrivere esplicitamente su/dev/tty, ignorando 'stdout' e' stderr'. –

3

Il messaggio è molto probabilmente dalla caratteristica pila protettore di GCC o da glib stesso. Se è da GCC, è uscita utilizzando la funzione fail(), che si apre direttamente /dev/tty:

fd = open (_PATH_TTY, O_WRONLY); 

_PATH_TTY non è molto standard, ma SingleUnix actually demands che /dev/tty esiste.

2

Ecco un esempio di codice che fa esattamente ciò che è stato chiesto (grazie a precedenti risposte che mi hanno indirizzato nella giusta direzione). Entrambi sono compilati con g ++ e stamperanno un messaggio sullo schermo anche quando stdout e stderr vengono reindirizzati.

Per Linux (Ubuntu 14):

#include <stdio.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <string.h> 

int main(int, char *[]) { 

    printf("This goes to stdout\n"); 

    fprintf(stderr, "This goes to stderr\n"); 

    int ttyfd = open("/dev/tty", O_RDWR); 
    const char *msg = "This goes to screen\n"; 
    write(ttyfd, msg, strlen(msg)); 
} 

Per Windows 7, usando MinGW:

#include <stdio.h> 
#include <fcntl.h> 
#include <string.h> 
#include <conio.h> 

void writeConsole(const char *s) { 
    while(*s) { 
     putch(*(s++)); 
    } 
} 

int main(int, char *[]) { 
    printf("This goes to stdout\n"); 

    fprintf(stderr, "This goes to stderr\n"); 

    writeConsole("This goes to screen\n"); 
} 
Problemi correlati