我在我的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");
}