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 机器上使用 gcc 4.1.2 进行 64 位构建(使用 -L/usr/lib64 和 -lstdc++ -lrt)并在 RHEL5 的 64 位安装上运行它,它工作正常
  • 如果我在 RHEL5.1 机器上使用 gcc 4.1.2 进行 32 位构建(使用 -L/usr/lib 和 -lstdc++ -lrt)并在完全相同的 64 位 RHEL5 机器上运行它,我们会从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,则问题在 RHEL 5.4 上消失

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

因此,看起来在 RHEL5.4 或它内部使用的信号量中引入了一个错误,并且 sem_init 没有正确初始化 sem_t 内存。或者, sem_timed 等待已更改为以前所未有的方式对此敏感。

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

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

像素

更新 - 这是测试用例代码,以防其他人想尝试。请注意,仅当从 .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

这是在 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创建libsemtest.so.

我已经在 RH 5.3 上对此进行了测试。

于 2011-04-25T07:09:22.323 回答