Подтвердить что ты не робот

Как программа Unix выводит вывод на экран, даже когда stdout и stderr перенаправляются?

На моей машине Ubuntu была запущена программа (valgrind, на самом деле) и перенаправлена ​​как stdout, так и stderr в разные файлы. Я был удивлен, увидев короткое сообщение на экране - как это возможно? Как я мог сделать это сам в программе на С++?

EDIT: Здесь используется команда, и вывод:

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

EDIT2: играя с ним немного больше, оказывается, что myprogram, а не valgrind, вызывает печать сообщения, и, как сказано ниже, похоже, что код обнаружения разлома gcc-стека печатает на/dev/tty

4b9b3361

Ответ 1

Это не написано valgrind, а скорее glibc, а ваша. /myprogram использует 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);

Ниже приведены некоторые релевантные части 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);

Ответ 2

Сообщение, скорее всего, связано с функцией защиты стека GCC или от самого glib. Если он из GCC, он выводится с помощью функции fail(), которая непосредственно открывается /dev/tty:

fd = open (_PATH_TTY, O_WRONLY);

_PATH_TTY не является стандартным, но SingleUnix фактически требует, что /dev/tty существует.

Ответ 3

Вот пример кода, который выполняет именно то, что было задано (благодаря более ранним ответам, указывающим на меня в правильном направлении). Оба скомпилированы с g++ и будут печатать сообщение на экране, даже когда stdout и stderr будут перенаправлены.

Для 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));
}

Для Windows 7 с помощью 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");
}