代码之家  ›  专栏  ›  技术社区  ›  pxb

在RedHat Enterprise Linux 5.3及更高版本上,是否不正确支持sem-timedwait?

  •  5
  • pxb  · 技术社区  · 15 年前

    我们在带有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

    更新:只是用以下结果做了更多的测试:

    • 如果我使用gcc 4.1.2在rhel5.4设备上(使用-l/usr/lib64和-lstc++-lrt)进行64位构建,并在rhel5的64位安装上运行它,那么它可以正常工作。
    • 如果我在rhel5.1框上使用gcc 4.1.2进行32位构建(使用-l/usr/lib和-lstdc++-lrt),并在完全相同的64位rhel5框上运行它,那么我们会从sem-timedwait获得enosys错误。

    因此,它似乎是在rhel5.4(和看似rhel5.3)上64位和32位运行时libs之间的差异。唯一的另一个区别是32位和64位构建分别是由rhel5.1和rhel5.4框完成的。

    2 回复  |  直到 12 年前
        1
  •  6
  •   pxb    15 年前

    最后找出问题所在。在RHEL 5.4上,如果我们调用sem_init,那么就执行sem_timed wait,根据代码所在的位置,无论拥有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)
    

    如果我在RHEL5.2上运行完全相同的代码,问题就会消失,Valgrind不会报告任何错误。

    如果在调用sem init之前对sem_t变量执行memset,那么问题将在rhel 5.4中消失。

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

    因此,看起来像是在rhel5.4上引入了一个bug,或者是它内部使用的信号量,sem_init没有正确初始化sem_t内存。或者,sem_timed wait已经改变了对这一点的敏感度,这在某种程度上是以前没有的。

    有趣的是,在任何情况下,sem_in it都不会返回一个错误来表明它不起作用。

    或者,如果预期的行为是sem_init不会初始化sem_t的内存,这取决于调用方,那么行为肯定会随着rhel 5.4而改变。

    PXB

    更新-这里是测试用例代码,以防其他人想要尝试它。注意这个问题只在从a.so调用sem-timedwait时出现,而且只有rhel5.4(也许5.3还没有测试过它),并且只有在构建为32位二进制文件时才会出现(当然是针对32位libs的链接)。

    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
    2. 从内部运行。那么做memset
    3. 从exe内部运行而不执行memset
    4. 从exe和do 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
    

    您可以看到所有案例现在都按预期工作!

        2
  •  2
  •   Troy Alford    12 年前

    似乎 semtest 正在呼叫 sem_init@GLIBC_2.1 ,请 和 libsemtest.so 正在呼叫 sem_init@GLIBC_2.0 .

    sem_timedwait() 似乎需要2.1版。

    我通过添加 -lpthread 创建的规则 LIB测试 .

    我在Rh 5.3上测试过这个。