sem_timedwait не поддерживается должным образом в RedHat Enterprise Linux 5.3 и выше? - PullRequest
5 голосов
/ 02 декабря 2009

Мы видим странное поведение в системах RedHat Enterprise Linux с pthreads sem_timedwait. Это происходит только в версии 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

Обновление: только что провели дополнительное тестирование со следующими результатами:

  • если я делаю 64-битную сборку с использованием gcc 4.1.2 на коробке RHEL5.4 (с -L / usr / lib64 и -lstdc ++ -lrt) и запускаю ее на 64-битной установке RHEL5, она работает нормально
  • если я делаю 32-битную сборку с использованием gcc 4.1.2 на коробке RHEL5.1 (с -L / usr / lib и -lstdc ++ -lrt) и запускаю ее на точно такой же 64-битной машине RHEL5, мы получаем Ошибки ENOSYS от sem_timedwait

Итак, похоже, что есть разница между 64-битными и 32-битными библиотеками времени выполнения на RHEL5.4 (и, по-видимому, RHEL5.3). Единственным другим отличием было то, что 32- и 64-битные сборки были сделаны из блоков RHEL5.1 и RHEL5.4 соответственно.

Ответы [ 2 ]

5 голосов
/ 07 декабря 2009

Наконец выяснил, в чем проблема. На 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 не сообщает об ошибках.

Если я сделаю memset для переменной sem_t перед вызовом sem_init, проблема исчезнет на 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

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. запустить изнутри .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

Вы видите, что все дела теперь работают как положено!

3 голосов
/ 25 апреля 2011

Кажется, что semtest звонит sem_init@GLIBC_2.1, и libsemtest.so звонит sem_init@GLIBC_2.0.

sem_timedwait(), кажется, требуется версия 2.1.

Я получил правильные результаты для всех четырех тестов, добавив -lpthread к правилу, которое создает libsemtest.so.

Я проверил это на RH 5.3.

...