Pregunta

Estamos viendo un comportamiento extraño en los sistemas RedHat Enterprise Linux con pthreads sem_timedwait. Es sólo ocurre con las versiones 5.3 en adelante.

Cuando creamos el semáforo en un hilo de fondo con sem_init, se devuelve ningún error. Cuando hacemos sem_timedwait, obtenemos un retorno inmediato con errno = 38 (ENOSYS) que indica que no es compatible.

Si hacemos lo mismo en el hilo principal, que funciona como se espera y obtenemos ningún error de sem_timedwait.

No vemos que en RHEL 5.2 o antes. Hemos tratado de compilar nuestro código con gcc 3.2.3 y 4.1.2 y obtener el mismo resultado, por lo que parece ser una cuestión de tiempo de ejecución.

Por lo tanto, mis preguntas (por fin;)

1) ¿Alguien más ha visto esto? 2) ¿es un problema conocido con RHEL 5.3 en adelante? 3) que estamos utilizando sem_timedwait a dormir un solo hilo. ¿Qué alternativas existen en Linux para hacer la misma cosa?

Si esto es un duplicado de otra pregunta, que me haga saber. He buscado pero no puede encontrar uno con la misma pregunta, a otros similares para OSX que no es lo que estamos usando.

gracias, pxb

Actualización: acaba de hacer algunas pruebas más con los siguientes resultados:

  • si hago una acumulación de 64 bits utilizando gcc 4.1.2 en un cuadro de RHEL5.4 (con -L / usr / lib64 y -lstdc ++ -lrt) y ejecutarlo en un poco de instalar 64 RHEL5 funciona bien
  • si hago una acumulación de 32 bits utilizando gcc 4.1.2 en un cuadro de RHEL5.1 (con -L / usr / lib y -lstdc ++ -lrt) y ejecutarlo en un exactamente el mismo cuadro de RHEL5 de 64 bits, obtenemos ENOSYS errores de sem_timedwait

Por lo tanto, parece haber una diferencia entre las bibliotecas de tiempo de ejecución 64 y 32 bits en RHEL5.4 (y aparentemente RHEL5.3). La única otra diferencia era que el bit 32 y 64 se realizaron construye de cajas RHEL5.1 y RHEL5.4 respectivamente.

¿Fue útil?

Solución

Finalmente descubrió cuál es el problema. En RHEL 5.4 si llamamos sem_init entonces sem_timedwait obtenemos un comportamiento un tanto aleatoria de la espera temporizada, dependiendo de donde se encuentra el código, si el objeto que posee el sem_t está en el montón o pila, etc A veces los cronometrados retornos de espera inmediatamente con errno = 38 (ENOSYS), a veces se espera correctamente antes de regresar.

Si lo ejecuta a través de valgrind da este error:

==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)

Si me quedo exactamente el mismo código en RHEL 5.2 el problema desaparece y valgrind informes de errores.

Si hago un memset sobre la variable sem_t antes de llamar sem_init el problema desaparece en RHEL 5.4

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

Por lo tanto, parece que un error se ha introducido con semáforos en RHEL5.4 o algo que se utiliza internamente y no se sem_init inicializar correctamente la memoria sem_t. O, espera sem_timed ha cambiado a ser sensibles a esto de una manera que no era antes.

Es interesante que en ningún caso hace sem_init devolver un error que indique que no funcionaba, aunque.

Por otra parte, si el comportamiento esperado es que no va a sem_init intialise la memoria de sem_t y que depende de la persona que llama, entonces el comportamiento ciertamente ha cambiado con RHEL 5.4

pxb

Actualizar - aquí está el código de caso de prueba por si alguien más quiere probarlo. Tenga en cuenta el problema sólo se produce cuando sem_timedwait se llama desde un .so, y sólo RHEL5.4 (tal vez 5.3 no han probado), y sólo cuando construido como un 32 bit binario (que une contra 32 libs bits por supuesto)

1) en 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) en main.cpp (tenga en cuenta la función de duplicado de prueba por lo que podemos comparar va desde dentro del .so con el 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) aquí está el 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

Los casos de prueba son:

  1. ejecutar desde dentro .so sin hacer memset
  2. ejecutar desde dentro .so y hacer memset
  3. ejecutar desde dentro exe sin hacer memset
  4. ejecutar desde dentro exe y hacer memset

Y aquí está el resultado que se ejecuta en 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

Se puede ver que el caso 1 vuelve inmediatamente con errno = 38.

Si corremos el mismo código en RHEL5.2 obtenemos lo siguiente:

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

Se puede ver que todos los casos ya funcionan correctamente!

Otros consejos

Parece que semtest está llamando sem_init@GLIBC_2.1, y libsemtest.so está llamando sem_init@GLIBC_2.0.

sem_timedwait() parece requerir la versión 2.1.

Me dieron resultados correctos para las cuatro pruebas añadiendo -lpthread a la regla que crea libsemtest.so.

He probado esto en RH 5.3.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top