如何保留文件描述符?

时间:2015-11-17 10:47:45

标签: c debugging posix file-descriptor

我正在写一个基于curses的程序。为了让我在这个程序中找到错误更简单,我想生成调试输出。由于程序已在终端上显示用户界面,我无法将调试输出放在那里。

相反,我打算无条件地将调试输出写入文件描述符3。您可以将program 3>/dev/ttyX调用该程序,/dev/ttyX是另一种电传打字机,以查看调试输出。当文件描述符3未打开时,写入调用失败并显示EBADF,我在编写调试输出时会忽略所有错误。

当我打开另一个文件并且没有请求调试输出时(即文件描述符3尚未打开),会出现问题。在这种情况下,新打开的文件可能会收到文件描述符3,导致调试输出随机损坏我刚刚打开的文件。这是件坏事。我怎么能避免这个?有没有一种可移植的方法将文件描述符标记为“保留”等?

以下是我的一些想法及其问题:

  • 在打开任何其他文件之前,我可以打开/dev/null或临时文件到文件描述符3(例如通过dup2())。这有效,但我不确定我是否可以认为这总是成功,因为开放/dev/null可能不会成功。
  • 我可以测试文件描述符3是否打开,如果不是,则不编写调试输出。当我尝试通过调用exec重新启动程序时,这是有问题的,因为在exec调用之前可能已经打开(并且未关闭)不同的文件描述符。我可以在调用exec之前故意关闭文件描述符3,当它尚未打开进行调试时,但这感觉真的很糟糕。

4 个答案:

答案 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,则会收到无效的文件描述符,并且您的调试调用会失败 - 可能是默默无闻。