终于知道是什么问题了。在 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
测试用例是:
- 从 .so 内部运行而不做 memset
- 从 .so 内部运行并执行 memset
- 从 exe 中运行而不执行 memset
- 从 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
您可以看到所有案例现在都按预期工作!