official - Uso de subprocesos mĂșltiples de C++ 11 en una biblioteca compartida cargada por programa sin soporte de subprocesos
c++20 (2)
Actualmente estoy tratando de usar subprocesos múltiples de C ++ 11 en una biblioteca compartida que se carga en el programa principal (escrito en C) en Linux. Esto es parte de un gran programa de simulación y no puedo cambiar nada sobre la carga de la biblioteca o cambiar el programa principal en general.
El programa principal está compilado con gcc 4.1.2 y no tengo las fuentes para ello (no puedo compilarlo con gcc 4.8.2).
La biblioteca compartida se compila con gcc 4.8.2 para usar el subprocesamiento múltiple de C ++ 11. Estoy pasando los comandos del compilador
-pthread -lpthread -std=c++11
como se explica en ¿Cuáles son las opciones de enlace correctas para usar std :: thread en GCC bajo linux?
La compilación de un programa de prueba independiente con esta configuración (" -pthread -std=c++11
" y gcc 4.8) funciona correctamente en mi sistema. Pero cuando inicio el programa cargando la biblioteca compartida, obtengo una excepción:
Caught std::exception!
Exception Message: Enable multithreading to use std::thread: Operation not permitted
Terminating...
El uso de los -pthread
y -lpthread
( Edición: y también solo -pthread
sin -lpthread
) no funciona. Los argumentos del compilador son (estoy usando el sistema de compilación de cocinero):
-pthread -std=c++11 -fmessage-length=0 -fPIC -Wchar-subscripts ...(lots of -W* here)
... -Wunused-variable -m64 -D__64BIT__ -pthread -lpthread
y los argumentos del enlazador (parámetros duplicados debido al sistema de compilación):
-pthread -lpthread -std=c++11 -pthread -lpthread -std=c++11 -shared -fPIC -Wl,-Bsymbolic -Wl,--allow-shlib-undefined -pthread -lpthread
llamando a ldd en mi biblioteca da el siguiente resultado
$ ldd calc3/build/amd64_linux26_RH5/library.so
linux-vdso.so.1 => (0x00007fff4d1fd000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00002ae6ec124000)
libstdc++.so.6 => /afs/bb/data/d6833/util/gcc_482/lib64/libstdc++.so.6 (0x00002ae6ec340000)
libm.so.6 => /lib64/libm.so.6 (0x00002ae6ec655000)
libgcc_s.so.1 => /afs/bb/data/d6833/util/gcc_482/lib64/libgcc_s.so.1 (0x00002ae6ec8d8000)
libc.so.6 => /lib64/libc.so.6 (0x00002ae6ecaef000)
/lib64/ld-linux-x86-64.so.2 (0x00000032cb400000)
y en el programa principal
$ ldd .../bin-64/main_program
linux-vdso.so.1 => (0x00007fff64595000)
libdl.so.2 => /lib64/libdl.so.2 (0x00000032cc000000)
libz.so.1 => /usr/lib64/libz.so.1 (0x00000032cc800000)
libc.so.6 => /lib64/libc.so.6 (0x00000032cb800000)
/lib64/ld-linux-x86-64.so.2 (0x00000032cb400000)
La biblioteca pthread está vinculada a mi biblioteca compartida pero no al programa principal. Esta answer indica que debe vincular pthreads al programa principal, pero el segundo comentario sobre esta respuesta (por @R ..) dice que no es necesario (que suena lógicamente).
Desafortunadamente, no sé nada sobre la mecánica de carga de todo el sistema, excepto que mi biblioteca está usando otra biblioteca de C ++ como API.
Tenga en cuenta que otras características de C ++ 11 funcionan (y libstdc ++. También está en las dependencias de mi biblioteca) pero el multiproceso de C ++ 11 no lo está (aunque libpthread.so también está en las dependencias de mi biblioteca).
El uso de una clase de subprocesos de una biblioteca contenida en el propio programa está funcionando (y esta clase de subprocesos también usa pthreads).
También he intentado usar -fabi-version=0
o -fabi-version=2
porque el programa principal está compilado con gcc 4.1.2 con mi biblioteca pero no cambió nada.
¿Hay algo que haya pasado por alto o una opción de compilación que pueda usar para que funcione? ¿O parece ser un problema de mi entorno de programa? Cualquier idea es bienvenida.
Editar:
Intenté usar -Wl,-no-as-needed
(como se sugiere en los comentarios) pero desafortunadamente no cambió nada.
Usar clang 3.5 en lugar de gcc 4.8 tampoco funcionó.
La creación de una pequeña aplicación de prueba que carga una biblioteca compartida (como en la respuesta a continuación por @chill) funciona (incluso sin el indicador del compilador) siempre que use gcc 4.8 o clang 3.5 para la aplicación principal y la biblioteca compartida. Cuando se usa gcc 4.1 para el programa principal, sin embargo, el programa principal no puede cargar la biblioteca (que funciona en mi aplicación "real"). Creo que podría haber un problema con las diferentes ABI de los compiladores.
El uso de pthreads directamente desde pthread.h
parece funcionar (aunque el programa termina actualmente en pthread_join
sin mensaje de error, pero todavía estoy probando allí ...)
Edición 2:
Ejecutar el ''programa de prueba'' con LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
(debido a que las rutas de la biblioteca de gcc 4.8 también deben estar allí, gracias a @MvG) ejecutó el programa pero se bloqueó nuevamente con la Enable multithreading to use std::thread: Operation not permitted
Excepción de Enable multithreading to use std::thread: Operation not permitted
.
Revisé todas las demás bibliotecas que están cargadas (las encontré con strace ./main_program 2>&1 | grep ''^open(".*/.so"''
[ver here ]) y las revisé todas con ldd
. Todas dependen de la mismas bibliotecas (con las mismas rutas). salidas ldd
(en todas ellas):
linux-vdso.so.1 => (0x00007fff4d3fd000)
libstdc++.so.6 => /afs/bb/data/d6833/util/gcc_482/lib64/libstdc++.so.6 (0x00002ade28774000)
libm.so.6 => /lib64/libm.so.6 (0x00002ade28ab0000)
libgcc_s.so.1 => /afs/bb/data/d6833/util/gcc_482/lib64/libgcc_s.so.1 (0x00002ade28d33000)
libc.so.6 => /lib64/libc.so.6 (0x00002ade28f49000)
/lib64/ld-linux-x86-64.so.2 (0x00000032ea200000)
(que todos no dependen de libpthread.so.0 excepto mi biblioteca y otra (pero es el mismo /lib64/libpthread.so.0
))
Algunas bibliotecas tienen más dependencias (que no parecen estar relacionadas con subprocesos) pero no parece haber dependencias "en conflicto" (no hay dependencias para diferentes versiones / rutas de la misma biblioteca en ninguna de estas bibliotecas).
En thread.cc
puede leer que esta excepción se genera si __gthread_active_p
devuelve false. Esa llamada simplemente verifica si un símbolo dado está disponible o no. El símbolo en cuestión es un símbolo débil: no tiene que estar presente, pero su presencia se verifica para decidir si se admite o no el subproceso.
Pero, ¿qué significa la presencia de un símbolo? En este caso, significa que el símbolo está en la lista de tablas de símbolos que la biblioteca en cuestión ( libgcc_s.so.1
en mi caso) busca para definiciones de símbolos. Esto incluye los símbolos exportados por la aplicación en sí, pero también los símbolos exportados por todas las bibliotecas cargadas antes . Sin embargo, no incluye bibliotecas cargadas después . Desafortunadamente, si libgcc
se carga antes que libpthread
, entonces el símbolo no está disponible en su dominio de búsqueda. Por lo que informa de subprocesos como no compatible. Supongo que tienes algún otro módulo de C ++ cargado antes del multihilo, por lo que te encuentras con este problema.
Una solución que funciona en reproduce es establecer LD_PRELOAD=/lib64/libpthread.so.0
en el entorno utilizado para llamar al binario. Eso carga a libpthread
por adelantado, por lo que sus símbolos están disponibles para satisfacer el vínculo de símbolo débil. Esto no funcionará para los binarios setuid / setgid, y podría considerarse un truco feo en otros casos también, así que estoy interesado en soluciones más limpias. Sin embargo, esto hace el trabajo la mayor parte del tiempo.
He podido reproducir algo muy similar a tu situación.
Primero la fuente de la biblioteca compartida de prueba:
#include <thread>
void f() {}
extern "C" int foo () {
std::thread(f).join();
return 0;
}
Compilado / enlazado con c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so
Luego el "programa principal":
#include <dlfcn.h>
int
main () {
void *lib = dlopen ("libthrlib.so", RTLD_NOW);
int (*foo)();
foo = (int (*)()) dlsym (lib, "foo");
foo ();
}
Compilado / enlazado con gcc main.c -ldl
El resultado de la ejecución es:
velco@sue:~/tmp$ LD_LIBRARY_PATH=`pwd` ./a.out
terminate called after throwing an instance of ''std::system_error''
what(): Enable multithreading to use std::thread: Operation not permitted
Aborted (core dumped)
Ahora, genere la biblioteca compartida, agregando a la línea de comandos las opciones -Wl,-no-as-needed
c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so -Wl,-no-as-needed
Y el programa principal se ejecuta sin error.
Ejecuto esto en Ubuntu 13.10 con gcc 4.8.1, todo lo anterior puede no ser aplicable a su entorno, pero los efectos son muy similares a los suyos, por lo que es muy probable que la misma solución funcione para usted, vale la pena intentarlo de todos modos :)
PD. Otra cosa que vale la pena probar, agregue una referencia explícita para pthread_cancel
en su biblioteca:
#include <pthread.h>
void *zzz = (void *) pthread_cancel;