我正在写一个基于curses的程序。为了让我在这个程序中找到错误更简单,我想生成调试输出。由于程序已在终端上显示用户界面,我无法将调试输出放在那里。
相反,我打算无条件地将调试输出写入文件描述符3。您可以将program 3>/dev/ttyX
调用该程序,/dev/ttyX
是另一种电传打字机,以查看调试输出。当文件描述符3未打开时,写入调用失败并显示EBADF
,我在编写调试输出时会忽略所有错误。
当我打开另一个文件并且没有请求调试输出时(即文件描述符3尚未打开),会出现问题。在这种情况下,新打开的文件可能会收到文件描述符3,导致调试输出随机损坏我刚刚打开的文件。这是件坏事。我怎么能避免这个?有没有一种可移植的方法将文件描述符标记为“保留”等?
以下是我的一些想法及其问题:
/dev/null
或临时文件到文件描述符3(例如通过dup2()
)。这有效,但我不确定我是否可以认为这总是成功,因为开放/dev/null
可能不会成功。exec
重新启动程序时,这是有问题的,因为在exec
调用之前可能已经打开(并且未关闭)不同的文件描述符。我可以在调用exec
之前故意关闭文件描述符3,当它尚未打开进行调试时,但这感觉真的很糟糕。答案 0 :(得分:3)
为什么要使用fd 3?为什么不使用fd 2(stderr)?它已经有一个定义明确的"我正在记录某些种类和#34;意思是,总是(不是真的,但足够真实......)并且你可以在启动二进制文件之前重定向它,以获得你想要的日志。
另一种选择是使用LOG_DEBUG级别将消息记录到syslog。这需要调用syslog()
而不是正常的写函数,但这只是使日志记录更加明确。
检查stderr是否已被重定向或仍然指向终端的一种简单方法是使用isatty
函数(下面的示例代码):
#include <stdio.h>
#include <unistd.h>
int main(void) {
if (isatty(2)) {
printf("stderr is not redirected.\n");
} else {
printf("stderr seems to be redirected.\n");
}
}
答案 1 :(得分:2)
在程序的最开始,打开/dev/null
,然后将其分配给文件描述符3:
int fd = open ("/dev/null", O_WRONLY);
dup2(fd, 3);
这样,文件描述符3就不会被采用。
然后,如果需要,重用dup2()
将文件描述符3分配给调试输出。
答案 2 :(得分:1)
您声称无法保证您可以成功打开/dev/null
,这有点奇怪,但让它运行起来。您应该可以使用socketpair()
来获取一对FD。然后,您可以将该对的写入结束设置为非阻塞,并dup2
。您声称已经忽略了对此FD的写入错误,因此位桶中的数据不会让您感到烦恼。你当然可以关闭socketpair的另一端。
答案 3 :(得分:1)
不要专注于特定的文件描述符值 - 无论如何都无法以可移植的方式控制它。如果你可以控制它。但是您可以使用环境变量来控制文件的调试输出:
int debugFD = getDebugFD();
...
int getDebugFD()
{
const char *debugFile = getenv( "DEBUG_FILE" );
if ( NULL == debugFile )
{
return( -1 );
}
int fd = open( debugFile, O_CREAT | O_APPEND | O_WRONLY, 0644 );
// error checking can be here
return( fd );
}
现在您可以将调试输出写入debugFD
。我假设您已经足够了解,以确保debugFD
在您需要的位置可见,以及如何确保在尝试使用它之前对其进行初始化。
如果您没有通过DEBUG_FILE
envval,则会收到无效的文件描述符,并且您的调试调用会失败 - 可能是默默无闻。