即使重定向了标准输出和标准错误,Unix程序如何在屏幕上显示输出?



我在我的Ubuntu机器上运行一个程序(实际上是valgrind),并将标准输出和标准错误重定向到不同的文件。我很惊讶地看到一条短信出现在屏幕上——这怎么可能呢?我怎么能在一个c++程序中做到这一点呢?

编辑:下面是我使用的命令和输出:

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

EDIT2:再多玩一点,结果是myprogram,而不是valgrind,导致消息被打印,并且如下所示,看起来gcc堆栈破坏检测代码正在打印到/dev/tty

它不是由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 == '')
  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 terminatedn",
            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 == '')
    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);

该消息很可能来自GCC的堆栈保护功能或glib本身。如果它来自GCC,则使用fail()函数输出,该函数直接打开/dev/tty:

fd = open (_PATH_TTY, O_WRONLY);

_PATH_TTY不是真正的标准,但SingleUnix实际上要求/dev/tty存在。

下面是一些示例代码,它们完全符合问题(感谢前面的答案为我指明了正确的方向)。它们都是用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 stdoutn");
    fprintf(stderr, "This goes to stderrn");
    int ttyfd = open("/dev/tty", O_RDWR);
    const char *msg = "This goes to screenn";
    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 stdoutn");
    fprintf(stderr, "This goes to stderrn");
    writeConsole( "This goes to screenn");
}

最新更新