3

この質問は、 Bad file descriptorと似ていますが、まったく同じではありません。これが「悪い質問」であることはわかっていますが(「ローカライズされすぎている」可能性があります)、理解できず、今ではアイデアがありません。


序章

75 の他のスレッドを開始するマネージャー スレッドがあります。これらの各スレッドは多くのことを行うため、関連するスレッドのみを説明します。

注意: いくつかのスレッド (たとえば 3、5、または 10) のみを開始した場合、このエラーは表示されません。これはマルチスレッドの問題だと思いますが、そうではないようです..そして、次のセクションでその理由がわかります。

したがって、次の 2 つのケースでは、このエラーが表示されることがありBad file descriptorます。


ケース1

エラーが表示されますTinyXML

すべてのスレッドで必要な xml ファイルがあります。これらのスレッドはすべてTinyXML、ファイルの解析に使用されます。これらのスレッドはすべて、このファイルを読み取り専用で使用します。(これは最適化できることは知っていますが、何でも)。

したがって、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?
    // ...
}

ケース 2

これはもう少し複雑です。戻り値のチェックを削除しましたが、実際のコードにはあるので問題ありません

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
}

長い質問で申し訳ありません。何か案は?

4

3 に答える 3

6

探すべきことの 1 つは、同じファイル記述子を 2 回閉じるコードです。

シングル スレッド プログラムでは、これは無害なプログラミングclose()ミスEBADFですclose()。ただし、マルチスレッド プログラムでは、閉じた記述子の記述子番号を の 2 つの呼び出しの間に別のスレッドに割り当てることができるclose()ため、2 回目close()は別のスレッドからの無関係なソケットを閉じます。他のスレッドの記述子をさらに読み書きすると、「不正なファイル記述子」エラーが発生します。

于 2012-12-07T19:21:15.747 に答える
3

ファイル記述子の理解に関するいくつかの言葉:

ファイルはグローバルリソースです。このような処理を行うために、(プロセス)グローバルインデックスが使用されます。ファイル記述子と呼ばれる整数値。スレッドがファイルを開く場合、この開かれたファイルはインデックスによって参照されます。このインデックスは、(スレッドではなく)プロセスに固有です。ファイルが閉じられると、ファイル記述子(整数インデックス)は使用されなくなり、プロセス(およびそのスレッド)で再利用される可能性があります。

例:

プロセス内の任意のスレッドによって、最初の呼び出しはopen()3を返す可能性があり、2番目の呼び出しは4を返す可能性があります。

その後3が閉じられると、への3番目の呼び出しはopen()再び3を返す可能性があります。

1回目の呼び出しがスレッド1によって、2回目がスレッド2によって、3回目がスレッド3によって行われる場合、3の値はすでにリサイクルされている可能性があるため、スレッド1はファイル記述子を再度閉じないことを理解しやすくなります。スレッド3による使用。これはclose()、スレッド1による2回目の(エラーのある)呼び出しによって閉じられた可能性があるため、無効なファイル記述子にアクセスしようとします。;-)

いくつかのサンプルコードを設定し、ファイル記述子へopen()の呼び出しによって返され、ファイル記述子として割り当てられた整数値を検査/ログに記録して、それがどのように機能するかを確認してください。

これはstdinstdoutおよびstderr、「事前定義された」ファイル記述子01およびを参照する場合もあります2。最近のLinuxの終了時stdinに、への呼び出しが続くと、ファイル記述子としてint fd = open("myfoofile.bar", ...)返される可能性があります。とにかく、カーネルまたはカーネルのいずれかが期待どおりにそのようなものを処理することはできません。たとえば、あいまいなエラーが発生する可能性があります。それを試してみてください!;->>0fdglibc0lseek(fd, ...)

于 2012-12-08T14:52:00.473 に答える
2

アプリケーションがマルチスレッドの場合、別のスレッドがファイルにアクセスしようとしているときに、一部のスレッドがファイルを閉じた場合に発生する可能性があります。

(アドレス空間のようなファイル記述子はグローバルであり、プロセスのすべてのスレッドに共通であるため)

straceどのようなシステムコールが行われるかを理解するために使用できます。

于 2012-12-07T18:26:12.503 に答える