如何保留文件描述符?

How to reserve a file descriptor?

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

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

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

这里有一些我的想法和他们的问题:

在程序的最开始,打开 /dev/null 然后将其分配给文件描述符 3:

 int fd = open ("/dev/null", O_WRONLY);
 dup2(fd, 3);

这样,文件描述符 3 就不会被占用。

然后,如果需要,重复使用 dup2() 将文件描述符 3 分配给您的调试输出。

你说你不能保证你能成功打开/dev/null,这有点奇怪,但让我们运行用它。您应该可以使用 socketpair() 来获得一对 FD。然后你可以设置非阻塞对的写端,并且 dup2 它。您声称您已经忽略了写入此 FD 的错误,因此进入位桶的数据不会打扰您。你当然可以关闭socketpair的另一端。

不要关注特定的文件描述符值 - 无论如何您都无法以可移植的方式控制它。如果你能完全控制它。但是您可以使用环境变量来控制调试输出到文件:

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,您会得到一个无效的文件描述符并且您的调试调用会失败 - 可能是无声的。

为什么要使用 fd 3?为什么不使用 fd 2 (stderr)?它已经有一个明确定义的 "I am logging of some sorts" 意思,总是(不是真的,但足够真实......)并且你可以在启动你的二进制文件之前重定向它,以获得你想要的日志。

另一种选择是使用 LOG_DEBUG 级别将消息记录到系统日志中。这需要调用 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");
  }
}