这个问题在某种程度上类似于Bad file descriptor,但它根本不相同。我知道这是“糟糕的问题”(可能是“过于本地化”),但我无法理解,现在我已经没有任何想法。 子>
我有一个经理线程,它启动了75个其他线程。这些线程中的每一个都做了很多事情,因此我只描述相关的线程。
请注意:如果我只启动几个线程 - 例如3或5或10,则不会出现此错误!这让我觉得,这是一些多线程问题,但似乎并不是这样。你会在下一节中看到原因。
因此,在以下2个案例中,有时我收到此错误Bad file descriptor
:
错误出现在TinyXML
有一个xml文件,所有线程都需要它。所有这些线程都使用TinyXML
来解析文件。所有这些线程都使用此文件 READ-ONLY!(我知道这可以优化,但无论如何)。
因此,导致Bad file descriptor
错误的代码是:
// ...
// NOTE: this is LOCAL, other threads do NOT have access to it
TiXmlDocument doc;
doc.LoadFile( filename );
// and here's the LoadFile:
bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )
{
//...
FILE* file = fopen( value.c_str (), "rb" );
if ( file )
{
// this IS executed, so file is NOT NULL for sure
bool result = LoadFile( file, encoding );
//...
}
//...
}
bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
{
// ...
long length = 0;
fseek( file, 0, SEEK_END );
// from the code above, we are SURE that file is NOT NULL, it's valid, but
length = ftell( file ); // RETURNS -1 with errno: 9 (BAD FILE DESCRIPTOR)
// how is this possible, as "file" is not NULL and it appears to be valid?
// ...
}
这有点复杂。我已经删除了返回值的检查,但我在实际代码中有它们,所以这不是问题
int hFileR = open( sAlarmFileName.c_str(), O_CREAT | O_RDONLY,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH );
// hFileR is > 0 for sure, so success
flock( hFileR, LOCK_EX ) /* the result is > 0 for sure, so success*/
// read the file into a string
while( (nRes = read(hFileR, BUFF, MAX_RW_BUFF_SIZE)) > 0 ) // ...
//Write new data to file: reopen/create file - write and truncate mode
int hFileW = open( sAlarmFileName.c_str(),
O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR |
S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH );
// hFileW is > 0 for sure, so success
do
{
int nWrtRes = write( hFileW,
szText + nBytesWritten, nSize - nBytesWritten );
// nWrtRes is always >= 0, so success
nBytesWritten += nWrtRes;
}
while( nSize > nBytesWritten );
close( hFileW ); // this one is successful too
if( flock(hFileR, LOCK_UN) == -1 )
{
// THIS FAILS and executes _Exit( FAILURE );
}
if( close( hFileR ) < 0 )
{
// if the previous one do not fail, this one is successful too
}
很抱歉这个问题很长。有什么想法吗?
答案 0 :(得分:5)
要查找的一件事是两次关闭相同文件描述符的代码。
在单线程程序中,这是一个无害的编程错误,因为第二个close()
除了返回EBADF
之外什么都不做,而且很多代码都没有费心去检查{无论如何,{1}}返回值。但是,在多线程程序中,关闭描述符的描述符编号可以在两次调用close()
之间的另一个线程中分配,因此第二个close()
将从另一个线程关闭一个不相关的套接字。进一步读取和写入其他线程的描述符将导致“错误的文件描述符”错误。
答案 1 :(得分:3)
关于理解文件描述符的一些词语:
文件是全球资源。为了处理这种情况,使用(进程)全局索引:整数值,称为文件描述符。如果线程打开文件,则此打开的文件由索引引用。此索引对于进程是唯一的(不到线程)。如果文件被关闭,则文件描述符(整数索引)不再被使用,并且可以被进程(及其任何线程)重用。
示例:
通过进程中的任何线程,对open()
的第一次调用可能返回3,第二次调用可能返回4.
如果关闭3,则对open()
的第3次呼叫可能会再次返回3。
如果第1次调用由线程1完成,第2次由线程2完成,第3次由线程3完成,则很容易理解线程1不应再次关闭其文件描述符,因为3的值可能已经是由线程3回收并使用,它将尝试访问无效的文件描述符,因为它可能已被线程1对close()
的第二次(错误)调用关闭。好吗? ; - )
尝试设置一些示例代码,并检查/记录调用open()
返回的整数值并将其指定为文件描述符,以便了解其工作原理。
注意:
这也可以引用stdin
,stdout
和stderr
,“预定义”文件描述符0
,1
和2
。在最近的Linux关闭stdin
之后调用int fd = open("myfoofile.bar", ...)
可能会将0
作为文件描述符fd
返回。无论如何,内核或glibc
都无法按预期处理这样的0
。例如,使用lseek(fd, ...)
可能会出现模糊错误。试试吧! ; - &GT;&GT;
答案 2 :(得分:2)
如果应用程序是多线程的,那么如果某个线程关闭了文件,而另一个线程仍然试图访问它,则可能会发生这种情况。
(因为文件描述符,如地址空间,是全局的,并且对于进程的所有线程都是通用的)
您可以使用strace
来了解已完成的系统调用。