【问题标题】:How can a Unix program display output on screen even when stdout and stderr are redirected?即使 stdout 和 stderr 被重定向,Unix 程序如何在屏幕上显示输出?
【发布时间】:2015-07-10 02:40:16
【问题描述】:

我在我的 Ubuntu 机器上运行一个程序(实际上是 valgrind),并将 stdout 和 stderr 重定向到不同的文件。我惊讶地看到屏幕上出现一条短消息——这怎么可能?我怎么能自己在 C++ 程序中做到这一点?

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

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

EDIT2:再玩一点,结果是 myprogram 而不是 valgrind 导致了消息被打印,如下面的回答,看起来 gcc 堆栈粉碎检测代码正在打印到 /dev/tty

【问题讨论】:

  • 你能分享你使用的命令吗
  • 直接打开显示设备。例如echo 'foo' > /dev/pts/0
  • 好像是内核给的消息
  • 打开并写信给/dev/tty;删掉的答案其实是最简洁的。

标签: c++ linux


【解决方案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);

【讨论】:

  • 这是 ./myprogram 的 glibc 正在打印吗?而且它不继承valgrind的stderr/stdout?
  • 您可能想补充一点,它是通过打开_PATH_TTY(在大多数系统上为"/dev/tty")写入控制台的目录,因为这是实际问题。
  • @Yakk:是的,myprogram 使用 glibc,因此该例程在 ./myprogram 进程的上下文中调用。是的,它确实继承了valgrind 的标准输出和标准错误——但它不写入标准输出或标准错误。它似乎明确地打开了_PATH_TTY,这可能类似于/dev/tty。一般来说,程序可以显式写入/dev/tty,绕过stdoutstderr
  • 啊,错过了! stackoverflow.com/questions/307006/… 表示您可以通过明智地使用 screen 来绕过此功能。
【解决方案2】:

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

fd = open (_PATH_TTY, O_WRONLY);

_PATH_TTY 并不是真正的标准,但 SingleUnix actually demands 存在 /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");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-14
      • 1970-01-01
      • 1970-01-01
      • 2013-08-23
      • 2017-05-24
      • 1970-01-01
      • 2019-06-08
      • 2015-09-02
      相关资源
      最近更新 更多