5

pthreads sem_timedwait を使用する RedHat Enterprise Linux システムで奇妙な動作が見られます。バージョン 5.3 以降でのみ発生します。

sem_init を使用してバックグラウンド スレッドでセマフォを作成すると、エラーは返されません。sem_timedwait を実行すると、サポートされていないことを示す errno = 38 (ENOSYS) がすぐに返されます。

メイン スレッドで同じことを行うと、期待どおりに動作し、sem_timedwait からエラーは発生しません。

RHEL 5.2 以前では見られません。コードを gcc 3.2.3 および 4.1.2 でコンパイルしようとしましたが、同じ結果が得られたので、実行時の問題のようです。

それで、私の質問(最後に;)

1) 他の誰かがこれを見たことがありますか? 2) RHEL 5.3 以降の既知の問題ですか? 3) sem_timedwait を使用して単一のスレッドをスリープさせています。Linux で同じことを行うには、どのような代替手段がありますか?

これが別の質問と重複している場合は、お知らせください。私は見ましたが、同じ質問を持つものを見つけることができません。私たちが使用しているものではないOSXの同様のものです。

ありがとう、pxb

更新:次の結果でさらにテストを行いました:

  • RHEL5.4 ボックス (-L/usr/lib64 および -lstdc++ -lrt を使用) で gcc 4.1.2 を使用して 64 ビット ビルドを実行し、RHEL5 の 64 ビット インストールで実行すると、正常に動作します。
  • RHEL5.1 ボックス (-L/usr/lib および -lstdc++ -lrt を使用) で gcc 4.1.2 を使用して 32 ビット ビルドを実行し、まったく同じ 64 ビット RHEL5 ボックスで実行すると、ENOSYS エラーが発生します。 sem_timedwait

したがって、RHEL5.4 (および一見 RHEL5.3) の 64 ビットと 32 ビットのランタイム ライブラリの違いのようです。その他の唯一の違いは、32 ビットと 64 ビットのビルドがそれぞれ RHEL5.1 ボックスと RHEL5.4 ボックスで行われたことです。

4

2 に答える 2

5

最後に問題が何であるかを見つけました。RHEL 5.4では、sem_initを呼び出してsem_timedwaitを実行すると、コードの場所、sem_tを所有するオブジェクトがヒープ上にあるかスタック上にあるかなどに応じて、時間指定待機の動作がややランダムになります。時間指定待機がすぐに返される場合があります。 errno = 38(ENOSYS)の場合、戻る前に正しく待機することがあります。

valgrindを介して実行すると、次のエラーが発生します。

==32459== Thread 2:
==32459== Syscall param futex(op) contains uninitialised byte(s)
==32459==    at 0x406C78: sem_timedwait (in /lib/libpthread-2.5.so)
==32459==    by 0x8049F2E: TestThread::Run() (in /home/stsadm/semaphore_test/semaphore_test)
==32459==    by 0x44B2307: nxThread::_ThreadProc(void*) (in /home/stsadm/semaphore_test/libcore.so)
==32459==    by 0x4005AA: start_thread (in /lib/libpthread-2.5.so)
==32459==    by 0x355CFD: clone (in /lib/libc-2.5.so)

RHEL 5.2でまったく同じコードを実行すると、問題は解決し、valgrindはエラーを報告しません。

sem_initを呼び出す前にsem_t変数でmemsetを実行すると、RHEL5.4で問題が解決します。

memset( &_semaphore, 0, sizeof( sem_t ) );

そのため、RHEL5.4または内部で使用されるセマフォにバグが導入されたようであり、sem_initがsem_tメモリを正しく初期化していないようです。または、sem_timed waitが、以前とは異なる方法でこれに敏感になるように変更されました。

興味深いことに、sem_initが機能しなかったことを示すエラーを返すことはありません。

あるいは、予想される動作がsem_initがsem_tのメモリを初期化せず、それが呼び出し元次第である場合、動作はRHEL5.4で確実に変更されています。

pxb

更新-他の誰かが試してみたい場合のテストケースコードは次のとおりです。この問題は、sem_timedwaitが.soから呼び出され、RHEL5.4(5.3ではテストされていない可能性があります)のみ、および32ビットバイナリ(もちろん32ビットライブラリに対してリンク)としてビルドされた場合にのみ発生することに注意してください。

1)semtest.cpp内

#include <semaphore.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>

void semtest( int semnum, bool initmem )
{
        sem_t sem;

        if ( initmem )
        {
                memset( &sem, 0, sizeof( sem_t ) );
                printf( "sem %d: memset size = %d\n", semnum, sizeof( sem_t ) );
        }

        errno = 0;
        int res = sem_init( &sem, 0, 0 );

        printf( "sem %d: sem_init res = %d, errno = %d\n", semnum, res, errno );

        timespec ts;
        clock_gettime( CLOCK_REALTIME, &ts );
        ts.tv_sec += 1;

        errno = 0;
        res = sem_timedwait( &sem, &ts );

        printf( "sem %d: sem_timedwait res = %d, errno = %d\n\n", semnum, res, errno );
}

2)main.cpp内(.so内からの実行とexe内からの実行を比較できるように重複テスト関数に注意してください)

#include <semaphore.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>

extern void semtest( int semnum, bool initmem );

void semtest_in_exe( int semnum, bool initmem )
{
        sem_t sem;

        if ( initmem )
        {
                memset( &sem, 0, sizeof( sem_t ) );
                printf( "sem %d: memset size = %d\n", semnum, sizeof( sem_t ) );
        }

        errno = 0;
        int res = sem_init( &sem, 0, 0 );

        printf( "sem %d: sem_init res = %d, errno = %d\n", semnum, res, errno );

        timespec ts;
        clock_gettime( CLOCK_REALTIME, &ts );
        ts.tv_sec += 1;

        errno = 0;
        res = sem_timedwait( &sem, &ts );

        printf( "sem %d: sem_timedwait res = %d, errno = %d\n\n", semnum, res, errno );
}

int main(int argc, char* argv[], char** envp)
{
        semtest( 1, false );
        semtest( 2, true );
        semtest_in_exe( 3, false );
        semtest_in_exe( 4, true );
}

3)これがMakefileです

all: main

semtest.o: semtest.cpp
        gcc -c -fpic -m32 -I /usr/include/c++/4.1.2 -I /usr/include/c++/4.1.2/i386-redhat-linux semtest.cpp -o semtest.o

libsemtest.so: semtest.o
        gcc -shared -m32 -fpic -lstdc++ -lrt semtest.o -o libsemtest.so

main: libsemtest.so
        gcc -m32 -L . -lsemtest main.cpp -o semtest

テストケースは次のとおりです。

  1. memsetを実行せずに.so内から実行
  2. .so内から実行し、memsetを実行します
  3. memsetを実行せずにexeファイル内から実行
  4. exe内から実行し、memsetを実行します

そして、これがRHEL5.4で実行された結果です

sem 1: sem_init res = 0, errno = 0
sem 1: sem_timedwait res = -1, errno = 38

sem 2: memset size = 16
sem 2: sem_init res = 0, errno = 0
sem 2: sem_timedwait res = -1, errno = 110

sem 3: sem_init res = 0, errno = 0
sem 3: sem_timedwait res = -1, errno = 110

sem 4: memset size = 16
sem 4: sem_init res = 0, errno = 0
sem 4: sem_timedwait res = -1, errno = 110

ケース1がerrno=38ですぐに戻ることがわかります。

RHEL5.2でまったく同じコードを実行すると、次のようになります。

sem 1: sem_init res = 0, errno = 0
sem 1: sem_timedwait res = -1, errno = 110

sem 2: memset size = 16
sem 2: sem_init res = 0, errno = 0
sem 2: sem_timedwait res = -1, errno = 110

sem 3: sem_init res = 0, errno = 0
sem 3: sem_timedwait res = -1, errno = 110

sem 4: memset size = 16
sem 4: sem_init res = 0, errno = 0
sem 4: sem_timedwait res = -1, errno = 110

すべてのケースが期待どおりに機能することがわかります。

于 2009-12-07T16:49:53.210 に答える
3

semtest呼んsem_init@GLIBC_2.1でいるようで、libsemtest.so呼んでいるsem_init@GLIBC_2.0

sem_timedwait()バージョン 2.1 が必要なようです。

-lpthreadを作成するルールに追加することで、4 つのテストすべてで正しい結果が得られましたlibsemtest.so

これを RH 5.3 でテストしました。

于 2011-04-25T07:09:22.323 に答える