如何保留文件描述符?
How to reserve a file descriptor?
我正在编写一个基于 curses 的程序。为了让我更容易找到这个程序中的错误,我想生成调试输出。由于程序已经在终端上显示用户界面,我无法将调试输出放在那里。
相反,我打算无条件地将调试输出写入文件描述符 3。您可以将程序调用为 program 3>/dev/ttyX
,而 /dev/ttyX
是不同的电传打字机,以查看调试输出。当文件描述符 3 未打开时,写入调用失败 EBADF
,我在编写调试输出时忽略了所有错误。
当我打开另一个文件并且没有请求调试输出时出现问题(即文件描述符 3 没有被打开)。在这种情况下,新打开的文件可能会收到文件描述符 3,导致调试输出随机损坏我刚刚打开的文件。这是一件坏事。我怎样才能避免这种情况?有没有一种可移植的方法来将文件描述符标记为“保留”或类似的?
这里有一些我的想法和他们的问题:
- 我可以在打开任何其他文件之前打开
/dev/null
或文件描述符 3 的临时文件(例如,通过 dup2()
)。这行得通,但我不确定我是否可以假设它总是成功,因为打开 /dev/null
可能不会成功。
- 我可以测试文件描述符 3 是否打开,如果没有打开则不写入调试输出。当我试图通过调用
exec
重新启动程序时,这是有问题的,因为在 exec
调用之前可能已经打开(而不是关闭)不同的文件描述符。当文件描述符 3 尚未打开进行调试时,我可以在调用 exec
之前故意关闭它,但这感觉真的很难看。
在程序的最开始,打开 /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");
}
}
我正在编写一个基于 curses 的程序。为了让我更容易找到这个程序中的错误,我想生成调试输出。由于程序已经在终端上显示用户界面,我无法将调试输出放在那里。
相反,我打算无条件地将调试输出写入文件描述符 3。您可以将程序调用为 program 3>/dev/ttyX
,而 /dev/ttyX
是不同的电传打字机,以查看调试输出。当文件描述符 3 未打开时,写入调用失败 EBADF
,我在编写调试输出时忽略了所有错误。
当我打开另一个文件并且没有请求调试输出时出现问题(即文件描述符 3 没有被打开)。在这种情况下,新打开的文件可能会收到文件描述符 3,导致调试输出随机损坏我刚刚打开的文件。这是一件坏事。我怎样才能避免这种情况?有没有一种可移植的方法来将文件描述符标记为“保留”或类似的?
这里有一些我的想法和他们的问题:
- 我可以在打开任何其他文件之前打开
/dev/null
或文件描述符 3 的临时文件(例如,通过dup2()
)。这行得通,但我不确定我是否可以假设它总是成功,因为打开/dev/null
可能不会成功。 - 我可以测试文件描述符 3 是否打开,如果没有打开则不写入调试输出。当我试图通过调用
exec
重新启动程序时,这是有问题的,因为在exec
调用之前可能已经打开(而不是关闭)不同的文件描述符。当文件描述符 3 尚未打开进行调试时,我可以在调用exec
之前故意关闭它,但这感觉真的很难看。
在程序的最开始,打开 /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");
}
}