在RedHat Enterprise Linux 5.3之上不能正确支持sem_timedwait?

时间:2009-12-02 11:38:24

标签: c linux pthreads semaphore rhel5

我们在使用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框上使用gcc 4.1.2进行64位构建(使用-L / usr / lib64和-lstdc ++ -lrt)并在64位安装RHEL5上运行它可以正常工作
  • 如果我在RHEL5.1框上使用gcc 4.1.2进行32位构建(使用-L / usr / lib和-lstdc ++ -lrt)并在完全相同的64位RHEL5框上运行它,我们得到来自sem_timedwait的ENOSYS错误

因此,RHEL5.4上的64位和32位运行时库(以及看似RHEL5.3)之间似乎存在差异。唯一的另一个区别是32位和64位版本分别由RHEL5.1和RHEL5.4框完成。

2 个答案:

答案 0 :(得分: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,则问题会在RHEL 5.4上消失

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

因此,看起来在RHEL5.4上引入了一个带有信号量的bug或者它在内部使用的东西,而sem_init没有正确初始化sem_t内存。或者,sem_timed等已经改变为以前所未有的方式对此敏感。

有趣的是,在任何情况下,sem_init都不会返回错误以指示它不起作用。

或者,如果预期的行为是sem_init不会初始化sem_t的内存并且这取决于调用者,那么RHEL 5.4的行为肯定会改变

PXB

更新 - 这是测试案例代码,以防其他人想要尝试它。注意问题只发生在从.so调用sem_timedwait时才会出现,只有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. 从内部运行.so而不执行memset
  2. 从.so中运行并执行memset
  3. 从exe内部运行而不进行memset
  4. 从exe内部运行并执行memset
  5. 这是在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
    

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

答案 1 :(得分:3)

似乎semtest正在调用sem_init@GLIBC_2.1, 并且libsemtest.so正在调用sem_init@GLIBC_2.0

sem_timedwait()似乎需要2.1版。

通过将-lpthread添加到创建libsemtest.so的规则中,我获得了所有四项测试的正确结果。

我在RH 5.3上测试了这个。