El proyecto netcore llama a la biblioteca dinámica de linux bajo linux

El contenido del artículo puede parecer aburrido y hay algunos problemas en la composición tipográfica, pero si encuentra problemas relacionados y realmente no puede resolverlos, es mejor que se calme y lea este artículo detenidamente, obtendrá algo y puede saltar primero Vaya al final del artículo y vea si es de valor para su pregunta. .

Usar el método P/invocar

Si la llamada falla, es posible que el archivo so carezca de algunos archivos dependientes, que se pueden ver a través del comando ldd

ldd libzmq.so

Si no se pueden encontrar algunos archivos dependientes, aparecerán las palabras no encontrado, como las siguientes

/usr/lib64/libstdc++.so.6: versión `GLIBCXX_3.4.20' no encontrada (requerida por */3rd-party/protobuf-2.4.1/src/.libs/libprotobuf.so.7)
/usr/lib64/ libstdc++.so.6: versión `GLIBCXX_3.4.20' no encontrada (requerida por */3rd-party/protobuf-2.4.1/src/.libs/libprotoc.so.7)

Puede usar el comando de cadena para averiguar si realmente falta una dependencia

strings /usr/lib64/libstdc++.so.6 |grep GLIBCXX 得到结果
 
GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_3.4.14
GLIBCXX_3.4.15
GLIBCXX_3.4.16
GLIBCXX_3.4.17
GLIBCXX_DEBUG_MESSAGE_LENGTH

De hecho, el archivo no se encuentra. En este caso, necesitamos usar el comando de búsqueda para encontrar el archivo dependiente.

find / -name libstdc++.so.6*

Si puede encontrar el archivo so dependiente, puede usar el comando cp para copiar el archivo en el directorio lib64

cp /usr/local/lib64/libstdc++.so.6.0.20 /usr/lib64 //复制文件

El directorio del sistema bajo Centos es /usr/lib64, el directorio del sistema puede ser diferente bajo Suse

Si hay archivos antiguos, puede usar el comando rm para eliminar primero los archivos antiguos

sudo rm -rf /usr/lib64/libstdc++.so.6  //删除旧文件

Finalmente, use el comando ln para vincular al nuevo archivo

sudo ln -s /usr/lib64/libstdc++.so.6.0.20 /usr/lib64/libstdc++.so.6 //链接到新版本 (libstdc++.so.6.0.20是复制到linux中的文件的文件名)

Después de hacer todo esto, puede probar si el comando dlopen puede abrir el archivo normalmente.Si se puede abrir normalmente, entonces el método dllimport se puede usar normalmente.

El código fuente de dllimport no se ha desarrollado y se sospecha que también llama al comando dlopen en Linux para llamar al archivo so.

Además de usar directamente el método dllimport para llamar, también puede usar el método delegado para llamar al archivo so

El siguiente es el código de prueba, que se puede comparar y explicar completamente. El método p/invoke llama al archivo so en .netcore

public class SoTester
     {
         private const string LibraryName = "libzmq";
 
         const int RTLD_NOW = 2; // for dlopen's flags
         const int RTLD_GLOBAL = 8;
 
         [DllImport(@"libdl.so.2")]
         public static extern IntPtr dlopen(string filename, int flags);
         [DllImport("libdl.so.2")]
         public static extern IntPtr dlsym(IntPtr handle, string symbol);
 
         [DllImport("libdl.so.2", EntryPoint = "dlopen")]
         private static extern IntPtr UnixLoadLibrary(String fileName, int flags);
 
         [DllImport("libdl.so.2", EntryPoint = "dlclose", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
         private static extern int UnixFreeLibrary(IntPtr handle);
 
         [DllImport("libdl.so.2", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
         private static extern IntPtr UnixGetProcAddress(IntPtr handle, String symbol);
 
         [DllImport("libdl.so.2", EntryPoint = "dlerror", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
         private static extern IntPtr UnixGetLastError();
 
         public delegate int sumHandler(int a, int b);
         public static sumHandler sumfunc = null;
 
         [DllImport("libNativeLib.so", EntryPoint = "sum", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
         public static extern int Sum(int a, int b);
 
         public void Start()
         {
             IntPtr libPtr = IntPtr.Zero;
 
             string libName = $"{AppContext.BaseDirectory}libNativeLib.so";
 
             libPtr = UnixLoadLibrary(libName, 2 | 8);
 
             //libPtr = dlopen(libName, RTLD_NOW);
 
             if (libPtr != IntPtr.Zero)
                 Console.WriteLine($"调用dlopen打开{libName}成功");
             else
                 Console.WriteLine($"调用dlopen打开{libName}失败");
 
             var sumPtr = UnixGetProcAddress(libPtr, "sum");
 
             if (sumPtr != IntPtr.Zero)
                 Console.WriteLine($"dlopen调用sum成功");
             else
                 Console.WriteLine($"dlopen调用sum失败");
 
             sumfunc = Marshal.GetDelegateForFunctionPointer<sumHandler>(sumPtr);
 
             int ret = sumfunc(1, 3);
 
             Console.WriteLine($"调用sum结果:{ret}");
 
             var sumRet = Sum(5, 7);
 
             Console.WriteLine($"DllImport调用sum结果:{sumRet}");
 
             //var libname2 = $"libc.so.6";
             var libname2 = $"{AppContext.BaseDirectory}libzmq.so";
             //var libname2 = $"{AppContext.BaseDirectory}libAdminConsole.so";
             var consolePtr = UnixLoadLibrary(libname2, 2 | 8);
             var erroPtr = UnixGetLastError();
             Console.WriteLine($"错误描述:{Marshal.PtrToStringAnsi(erroPtr)}");
 
             if (consolePtr != IntPtr.Zero)
                 Console.WriteLine($"打开{libname2}成功");
             else
                 Console.WriteLine($"打开{libname2}失败");
         }
   }

pd: Actualmente, he probado CentOS7 y suse12 SP3. Personalmente, si necesita usar P/INVOKE en el proyecto, es mejor ejecutarlo en suse. La versión gcc predeterminada de centos es relativamente baja y es muy problemático para actualizar gcc. Todos los componentes de suse están completos, pero la versión gratuita no puede actualizar los componentes y p/invoke se puede usar directamente. No se utilizan otras versiones de Linux.

Categoría:  NetCore

Compile con gcc bajo Linux para generar un archivo de biblioteca de vínculos dinámicos *.so y llámelo

  https://blog.csdn.net/flyztek/article/details/73612469

Compile con gcc bajo Linux para generar un archivo de biblioteca de vínculos dinámicos *.so y llámelo

La biblioteca dinámica *.so se encuentra a menudo cuando se programa con C y C++ en Linux. Recientemente, encontré varios artículos en el sitio web para presentar la compilación y vinculación de bibliotecas dinámicas. Finalmente entiendo estas cosas de las que no sabía mucho antes. Aquí Haga una nota, y también brinde un poco de ayuda para otros hermanos que están preocupados por la biblioteca dinámica de enlaces de biblioteca.
1. Compilación de la biblioteca dinámica

A continuación se utiliza un ejemplo para presentar cómo generar una biblioteca dinámica. Aquí hay un archivo de encabezado: so_test.h, tres archivos .c: test_a.c, test_b.c, test_c.c, compilamos estos archivos en una biblioteca dinámica: libtest.so.

//so_test.h:
#incluye "stdio.h"
void test_a();
void test_b();
void test_c();

//test_a.c:
#incluye "so_test.h"
void test_a()
{   printf("this está en test_a...\n"); } //test_b.c: #include "so_test.h" void test_b() {   printf("esto está en test_b...\n"); } //test_c. c: #incluye "so_test.h"

















  printf("esto está en test_c...\n");
}
Compile estos archivos en una biblioteca dinámica: libtest.so
$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

2. El enlace de la biblioteca dinámica
En 1., hemos generado con éxito una biblioteca de enlaces dinámicos libtest.so propia A continuación, llamamos a las funciones en esta biblioteca a través de un programa. El archivo fuente del programa es: test.c.

test.c:
#include "so_test.h"
int main()
{ test_a(); test_b(); test_c(); return 0; } Enlace test.c con la biblioteca dinámica libtest.so para generar el archivo ejecutable test: $ gcc test.c -L. -ltest -o prueba para probar si está conectado dinámicamente. Si libtest.so aparece en la lista, entonces la conexión debería ser normal. $ ldd test Ejecute la prueba y podrá ver cómo llama a las funciones en la biblioteca dinámica. 3. Análisis de parámetros de compilación Lo más importante es una opción de la línea de comandos de GCC: -shared Esta opción especifica generar una biblioteca de enlaces dinámicos (dejar que el enlazador genere una tabla de símbolos de exportación de tipo T y, a veces, generar una exportación de tipo W de enlace débil símbolo), los programas externos no pueden conectarse sin este indicador. equivalente a un ejecutable














-fPIC: indica que el código compilado es independiente de la posición. Si no se usa esta opción, el código compilado depende de la posición, por lo que la copia del código se usa para satisfacer las necesidades de diferentes procesos durante la carga dinámica y el propósito de la carga real. no se puede lograr compartir segmentos de código.

-L.: indica que la biblioteca a vincular está en el directorio actual

-ltest: el compilador tiene una regla de nomenclatura implícita al buscar una biblioteca de enlace dinámico, es decir, agregue lib delante del nombre dado y agregue .so después para determinar el nombre de la biblioteca Nombre

LD_LIBRARY_PATH: Esta variable de entorno indica la ruta donde el enlazador dinámico puede cargar la biblioteca dinámica.

Por supuesto, si tiene autoridad de root, puede modificar el archivo /etc/ld.so.conf y luego llamar a /sbin/ldconfig para lograr el mismo propósito, pero si no tiene autoridad de root, solo puede usar el método de salida LD_LIBRARY_PATH.

4. Tenga en cuenta que

a menudo se encuentran varios problemas al llamar a una biblioteca dinámica. A veces, el directorio donde se encuentra el archivo de encabezado de la biblioteca se ha incluido mediante "-I" include, y el archivo donde se encuentra la biblioteca se guía por el parámetro "-L" y el nombre de biblioteca "-l" especificado, pero cuando se ve a través del comando ldd, no se puede encontrar el archivo so que especificó para el enlace. Lo que debe hacer en este momento es especificarlo modificando el Archivo LD_LIBRARY_PATH o /etc/ld.so.conf El directorio de la biblioteca dinámica. Por lo general, hacer esto puede resolver el problema de que la biblioteca no se puede vincular.

En Linux, puede usar el comando de exportación para establecer este valor, ingrese en la terminal de Linux:
export LD_LIBRARY_PATH=/opt/au1200_rm/build_tools/bin: $LD_LIBRARY_PATH:   
y luego ingrese: export   
y se mostrará si la configuración es correcta   
El método de exportación fallará después de reiniciar, por lo que también puede usar vim /etc/bashrc para modificar la variable LD_LIBRARY_PATH.   
Por ejemplo: LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/au1200_rm/build_tools/bin.

Conceptos básicos de bibliotecas estáticas, bibliotecas dinámicas y bibliotecas bajo la plataforma del brazo bajo Linux

1. Conceptos básicos

1.1, que es una biblioteca

 Hay una gran cantidad de bibliotecas tanto en la plataforma Windows como  en la plataforma Linux .

Esencialmente, una biblioteca es una forma binaria de código ejecutable que el sistema operativo puede cargar en la memoria para su ejecución.

Dado que las plataformas de Windows y  Linux  son diferentes (principalmente las diferencias en compiladores, ensambladores y enlazadores), los binarios de las dos bibliotecas no son compatibles.

Este artículo está limitado a la biblioteca bajo Linux.

1.2 Tipos de bibliotecas

Hay dos tipos de bibliotecas en Linux: bibliotecas estáticas y bibliotecas compartidas (bibliotecas dinámicas).

La diferencia entre los dos radica en el momento en que se carga el código.

El código de la biblioteca estática se cargó en el programa ejecutable durante el proceso de compilación, por lo que el tamaño es relativamente grande.

Estático con .a como sufijo, por ejemplo: libhello.a

El código de la biblioteca compartida (biblioteca dinámica) se carga en la memoria cuando se ejecuta el programa ejecutable, y solo se hace referencia a él durante el proceso de compilación, por lo que el tamaño del código es pequeño.

Dynamic generalmente tiene el sufijo .so, por ejemplo: libhello.so

La ventaja de una biblioteca compartida (biblioteca dinámica) es que si diferentes aplicaciones llaman a la misma biblioteca, solo se necesita almacenar una instancia de la biblioteca compartida en la memoria.

Para usar diferentes versiones de la biblioteca en el mismo sistema, puede agregar el número de versión como el sufijo después del nombre del archivo de la biblioteca, por ejemplo: libhello.so.1.0, porque la conexión del programa tiene como valor predeterminado .so como sufijo del archivo. . Por lo que para utilizar estas librerías se suele utilizar la forma de establecer enlaces simbólicos.

ln -s libhello.so.1.0 libhello.so.1 ln -s libhello.so.1 libhello.so

1.3 ¿Cómo se generan los archivos de biblioteca estática y de biblioteca dinámica en Linux?

Tome el siguiente código como ejemplo para generar la biblioteca hello utilizada anteriormente:

/* Hola C */ 

#include "hola.h" 

void decir hola() 

printf("hola mundo"); 

}

Primero, use gcc para compilar el archivo, y puede usar cualquier parámetro de compilación legal al compilar, como -g para agregar código de depuración, etc.:

$gcc -c hola.c -o hola.o

1. Generar una biblioteca estática Utilice la herramienta ar para generar una biblioteca estática. De hecho, ar significa archivo

$ar cqs libhola.a hola.o

2. Genere una biblioteca dinámica. Use gcc para completarla. Dado que puede haber varias versiones, generalmente se especifica el número de versión:

$gcc -shared -o libhola.so.1.0 hola.o

1.4.¿Cómo se nombran los archivos de la biblioteca?¿Existe alguna especificación?

En Linux, los archivos de biblioteca generalmente se colocan en /usr/lib y /lib,

El nombre de la biblioteca estática generalmente es libxxxx.a, donde xxxx es el nombre de la biblioteca;

El nombre de la biblioteca dinámica generalmente es libxxxx.so.major.minor, donde xxxx es el nombre de la biblioteca, major es el número de versión principal y minor es el número de versión secundaria.

1.5 Cómo ubicar el archivo de la biblioteca compartida (biblioteca dinámica) cuando se ejecuta el programa ejecutable:

Cuando el sistema carga el código ejecutable (es decir, el archivo de la biblioteca), puede saber el nombre de la biblioteca de la que depende, pero también necesita saber la ruta absoluta. En este momento, el cargador dinámico del sistema (vinculador dinámico/ cargador) es necesario.

Para el programa ejecutable en formato elf, se completa con ld-linux.so*, que busca en el segmento DT_RPATH del archivo elf—variable de entorno LD_LIBRARY_PATH—/etc/ld.so.cache file list—/lib/,/usr El directorio /lib encuentra el archivo de la biblioteca y lo carga en la memoria

Tales como: exportar LD_LIBRARY_PATH='pwd'

Agregue el directorio de archivos actual como un directorio compartido

1.6 Utilice la herramienta ldd para verificar de qué bibliotecas dinámicas depende el programa ejecutable o de qué bibliotecas dinámicas depende la biblioteca dinámica:

El comando ldd puede ver las bibliotecas compartidas de las que depende un programa ejecutable,

Por ejemplo # ldd /bin/lnlibc.so.6

=> /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

=> /lib/ld-linux.so.2 (0×40000000)

Puede ver que el comando ln depende de la biblioteca libc y la biblioteca ld-linux

Use el siguiente comando para ver las dependencias de la plataforma arm

Nota: arm-linux-readelf -d busybox | grep Shared se puede omitir después de grep, preste atención a las mayúsculas de la primera letra de Shared

   

1.7 Use la herramienta nm para verificar los nombres de las funciones en la biblioteca estática y la biblioteca dinámica (la clase T indica que la función está definida en la biblioteca actual, la clase U indica que la función se llama y se define en otras bibliotecas, la clase W es la biblioteca actual definida en, anulada por funciones en otras bibliotecas). :

A veces es posible que necesite verificar qué funciones hay en una biblioteca. La herramienta nm puede imprimir todos los símbolos involucrados en la biblioteca. La biblioteca aquí puede ser estática o dinámica.

Hay muchos símbolos enumerados en nm, y hay tres comunes:

Uno se llama en la biblioteca, pero no se define en la biblioteca (lo que indica la necesidad de otro soporte de biblioteca), representado por U;

Una es la función definida en la biblioteca, representada por T, que es la más común;

El otro son los llamados símbolos de "estado débil", que aunque están definidos en la biblioteca, pueden estar sobrescritos por símbolos del mismo nombre en otras bibliotecas, representados por W.

Por ejemplo, supongamos que un desarrollador quiere saber si se hace referencia a printf() en la biblioteca hello mencionada anteriormente:

$nm libhello.so | impresión grep

Se encuentra que printf es un símbolo de tipo U, lo que indica que se hace referencia a printf, pero no está definido en la biblioteca.

Se puede inferir de esto que para usar la biblioteca hello normalmente, debe haber otro soporte de biblioteca.Use la herramienta ldd para verificar de qué bibliotecas depende hello:

$ldd hola libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

 A partir de los resultados anteriores, puede continuar para ver dónde se define finalmente printf. Si está interesado, puede continuar

1.8 Usando la herramienta ar, puede generar una biblioteca estática y, al mismo tiempo, puede verificar qué archivos .o están incluidos en la biblioteca estática, es decir, qué archivos fuente están compuestos.

Puede usar ar -t libname.a para ver qué archivos .o constituyen una biblioteca estática.

Se puede generar una biblioteca estática con ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o

Al programar bajo Linux, sobre el uso de librerías:

1. Los parámetros de la biblioteca en el comando gcc/g++:

-compartido: esta opción especifica generar una biblioteca de enlaces dinámicos (deje que el enlazador genere una tabla de símbolos de exportación de tipo T y, a veces, genere un símbolo de exportación de tipo W de conexión débil), y el programa externo no se puede conectar sin este indicador. equivalente a un ejecutable

-fPIC: indica que el código se compila en código independiente de la posición (independiente de la dirección). Si no se usa esta opción, el código compilado depende de la posición. Por lo tanto, cuando se realiza la carga dinámica, el código se copia para cumplir con necesidades de diferentes procesos, y no puede lograr el propósito de compartir segmentos de código real.

-L: especifica la ruta de la biblioteca de enlace, -L significa que la biblioteca a vincular está en el directorio actual

-ltest: especifique el nombre de la biblioteca de enlaces como prueba. El compilador tiene una regla de nomenclatura implícita cuando busca una biblioteca de enlaces dinámicos, es decir, agregue lib delante del nombre dado y agregue .so después para determinar el nombre de la biblioteca

LD_LIBRARY_PATH: esta variable de entorno indica la ruta donde el enlazador dinámico puede cargar la biblioteca dinámica.

Por supuesto, si tiene autoridad de root, puede modificar el archivo /etc/ld.so.conf y luego llamar a /sbin/ldconfig para lograr el mismo propósito.

Sin embargo, si no tiene privilegios de root, solo puede usar el método de modificar la variable de entorno LD_LIBRARY_PATH.

Al llamar a una biblioteca dinámica, hay varios problemas que se encuentran a menudo:

1. A veces, es obvio que el directorio donde se encuentra el archivo de cabecera de la biblioteca se ha ingresado mediante "-I" include, el archivo donde se encuentra la biblioteca se guía por el parámetro "-L" y el nombre de la biblioteca de "-l", pero cuando se ve a través del comando ldd, es decir, no puede encontrar el archivo so que especificó para el enlace. Lo que debe hacer en este momento es especificar el directorio de la biblioteca dinámica modificando el archivo LD_LIBRARY_PATH o /etc/ld.so.conf. Por lo general, hacer esto puede resolver el problema de que la biblioteca no se puede vincular.

2. El orden de la ruta de búsqueda al vincular bibliotecas estáticas:

1. ld buscará el parámetro -L en el comando gcc/g++;

2. Busque la variable de entorno LIBRARY_PATH de gcc, que especifica la ruta de búsqueda para el archivo de biblioteca de enlaces estáticos del programa;

exportar LIBRARY_PATH=$LIBRARY_PATH:data/home/billchen/lib

3. Busque el directorio de biblioteca predeterminado /lib /usr/lib /usr/local/lib, que se escribió en el programa al compilar gcc.

3. Orden de la ruta de búsqueda durante la vinculación dinámica y la ejecución:

1. La ruta de búsqueda de la biblioteca dinámica especificada al compilar el código objeto;

2. La variable de entorno LD_LIBRARY_PATH especifica la ruta de búsqueda de la biblioteca dinámica, que especifica la ruta de búsqueda del archivo de la biblioteca de vínculos dinámicos del programa;

exportar LD_LIBRARY_PATH=$LD_LIBRARY_PATH:data/home/billchen/lib

3. La ruta de búsqueda de la biblioteca dinámica especificada en el archivo de configuración /etc/ld.so.conf;

4. La ruta de búsqueda de biblioteca dinámica predeterminada /lib;

5. La ruta de búsqueda de biblioteca dinámica predeterminada /usr/lib.

En cuarto lugar, los problemas de la biblioteca estática y la biblioteca de enlaces dinámicos al mismo tiempo:

Cuando una biblioteca existe tanto una biblioteca estática como una biblioteca dinámica, por ejemplo, cuando libmysqlclient.a y libmysqlclient.so existen al mismo tiempo:

En Linux, cuando la biblioteca dinámica y la biblioteca estática existen juntas, el enlazador gcc/g++ vinculará la biblioteca dinámica de forma predeterminada.

Puede usar el siguiente método para pasar parámetros al enlazador para ver si vincular la biblioteca dinámica o la biblioteca estática.

-WI,-Bstatic -llibname //Especifique para permitir que gcc/g++ vincule la biblioteca estática

usar:

gcc/g++ prueba.c -o prueba -WI,-Bstatic -llibname

-WI,-Bdynamic -llibname //Especifique para permitir que gcc/g++ vincule la biblioteca dinámica

usar:

gcc/g++ prueba.c -o prueba -WI,-Bdynamic -llibname

Si desea agregarlo completamente de forma estática, use el parámetro -static, es decir, vincule todas las bibliotecas al programa ejecutable de manera estática, para que el programa ejecutable generado ya no dependa de ninguna biblioteca.El problema que tienen los colegas es que el programa compilado de esta manera Muy grande y ocupa espacio.

5. Variables de entorno relevantes:

Variable de entorno LIBRARY_PATH: especifique la ruta de búsqueda del archivo de la biblioteca de enlaces estáticos del programa

Variable de entorno LD_LIBRARY_PATH: especifique la ruta de búsqueda del archivo de la biblioteca de vínculos dinámicos del programa

6. Problema de actualización de la biblioteca dinámica:

Cuando se actualiza la biblioteca de vínculos dinámicos,

No puede usar cp newlib.so oldlib.so, lo que puede hacer que el núcleo del programa se caiga;

En su lugar, debe utilizar:

rm oldlib.so luego cp newlib.so oldlib.so

o

mv oldlib.so oldlib.so_bak luego cp newlib.so oldlib.so

¿Por qué no se puede usar cp newlib.so oldlib.so?

Al reemplazar el archivo so, si el archivo de biblioteca dinámica utilizado por el programa se reemplaza directamente por cp new.so old.so sin detener el programa, el programa en ejecución fallará.

Solución:

La solución es usar "rm+cp" o "mv+cp" para reemplazar el método de operación directa "cp".

Hay dos formas de usar la biblioteca dinámica del sistema Linux: vincular dinámicamente la biblioteca en tiempo de ejecución, cargar dinámicamente la biblioteca y usarla bajo el control del programa.

1. ¿Por qué el comando cp reemplaza directamente el archivo so utilizado por el programa cuando el programa no se detiene, lo que hace que el programa se bloquee?

Muchos estudiantes se han encontrado con este problema en su trabajo.Al reemplazar el archivo so, si el archivo de biblioteca dinámica utilizado por el programa se reemplaza directamente por cp new.so old.so sin detener el programa, provocará que el programa se bloquee. , saliendo

Esto está relacionado con la implementación del comando cp. cp no cambia el inodo del archivo de destino, y el archivo de destino de cp heredará los atributos del archivo sobrescrito en lugar del archivo de origen. De hecho, se implementa así:

strace cp libnew.so libold.so 2>&1 |grep open.*lib.*.so

abierto("libnew.so", O_RDONLY|O_LARGEFILE) = 3

abierto("libold.so", O_INCORRECTO|O_TRUNC|O_ARCHIVO GRANDE) = 4

Cuando cp usa "O_WRONLY|O_TRUNC" para abrir el archivo de destino, la imagen del archivo original se destruye accidentalmente. De esta forma, el enlazador dinámico ld.so no puede acceder a la entrada de la función en el archivo so. Esto conduce a una falla de segmentación y el programa falla. El mecanismo de ld.so para cargar archivos y "reubicarlos" es más complicado.

2. ¿Cómo reemplazar el archivo so sin detener el programa y asegurarse de que el programa no se bloquee?

La respuesta es usar "rm+cp" o "mv+cp" para reemplazar el método de operación directa "cp".

Al reemplazar el antiguo archivo so libold.so con el nuevo archivo so libnew.so, si se utiliza el siguiente método:

rm libold.so //Si el kernel usa libold.so, el nodo inode no se eliminará inmediatamente.

cp libnew.so libold.so

Con este método, el inodo del archivo de destino libold.so en realidad ha cambiado Aunque el archivo libold.so original no se puede ver con "ls", su inodo no se ha eliminado hasta que el kernel publica su referencia.

(Es decir: rm libold.so, en este momento, si ld.so se agrega a libold.so, el kernel hace referencia al nodo de inodo de libold.so, el inodo de rm libold.so no se ha eliminado realmente, cuando ld.so es correcto El inodo se eliminará cuando finalice la referencia de libold.so. De esta manera, el programa no fallará, porque todavía está usando el antiguo libold.so. Cuando se use libold.so la próxima vez, ha sido reemplazado y se usará el nuevo libold .so)

De la misma manera, mv simplemente cambia el nombre del archivo, su inodo permanece sin cambios y el nuevo archivo usa un nuevo inodo. De esta forma, el enlazador dinámico ld.so aún usa el inodo del archivo original para acceder al archivo so antiguo. Entonces el programa aún puede ejecutarse normalmente.

(Es decir: después de mv libold.so ***, si el programa usa una biblioteca dinámica, todavía usa el antiguo nodo de inodo. Cuando se use libold.so la próxima vez, se usará el nuevo libold.so)

En este punto, ¿por qué el sistema prohíbe tales operaciones cuando se usa el comando "cp new_exec_file old_exec_file" directamente y aparece el mensaje "cp: no se puede crear un archivo normal 'antiguo': archivo de texto ocupado". En este momento, el método que adoptamos sigue siendo usar "rm+cp" o "mv+cp" para reemplazar el "cp" directo, que es el mismo que el reemplazo del archivo so mencionado anteriormente.

Pero, ¿por qué el sistema evita que cp cubra los programas ejecutables pero no los archivos?

Esto se debe a que Linux tiene un mecanismo de paginación por demanda. El llamado "paginación por demanda" simplemente significa que para ahorrar sobrecarga de memoria física, el sistema no carga todas las páginas (páginas) en la memoria cuando el programa se está ejecutando, sino solo cuando el programa se encuentra en ejecución, solo se carga cuando el sistema tiene requisitos de acceso. La "paginación por demanda" requiere que la imagen del programa en ejecución (nota, no el archivo en sí) no se modifique accidentalmente, por lo que el kernel bloqueará el inodo de la imagen del programa después de iniciar el programa.

Para el archivo so, lo carga ld.so, y ld.so también es un programa de modo de usuario después de todo, y no tiene derecho a bloquear el inodo, y no debe acoplarse con el sistema de archivos subyacente del kernel.

gcc especifica la ruta del archivo de encabezado y la ruta de la biblioteca de vínculos dinámicos

   

Este artículo presenta en detalle el método para especificar el archivo de encabezado gcc en Linux y el orden de la ruta de búsqueda. Además, también resume el método de vinculación dinámica gcc y la especificación de ruta, y también analiza el orden de búsqueda de rutas. Este artículo contiene muchos ejemplos, que son altamente operativos, y espero que los lectores puedan leerlo por sí mismos.
1. #incluir <> y #incluir ""

#include <> Ir directamente a algunos directorios especificados por el sistema para encontrar algunos archivos de encabezado.
#include ""Primero vaya a la carpeta donde se encuentra el archivo de origen y luego vaya a algunos directorios especificados por el sistema para encontrar algunos archivos de encabezado.

2. Tres situaciones en las que gcc especifica el archivo de cabecera:

1. Se asignará a la carpeta /usr/include de forma predeterminada (el nivel más profundo es una ruta relativa, la ruta del programa ejecutable gcc es /usr/bin/gcc, luego especifica la ruta del encabezado del archivo de encabezado cuando realmente funciona Un método de ruta relativa, convertido a una ruta absoluta es agregar /usr/include, como #include es incluir /usr/include/stdio.h)

2. GCC también usa -I para especificar la ruta, es decir,
la carpeta donde se encuentra el archivo de encabezado gcc -I (se puede usar la ruta absoluta o la ruta relativa) archivo fuente
Como ejemplo:
deje que la ruta actual sea /root/ test, y su estructura es la siguiente:
include_test.c
include/include_test.h
Hay dos formas de acceder a include_test.h.
1. #include "include/include_test.h" en include_test.c y luego gcc include_test.c
2. #include o #include en include_test.c y luego gcc –I include include_test.c también está disponible

3. Parámetros: -nostdinc hace que el compilador ya no busque archivos de encabezado en el directorio de archivos de encabezado predeterminado del sistema. Generalmente se usa junto con -I para limitar claramente la ubicación de los archivos de encabezado.

Al compilar el módulo del controlador, debido a requisitos especiales, se debe forzar a GCC a no buscar la ruta predeterminada del sistema, es decir, a no buscar /usr/include, usar el parámetro -nostdinc y usar el parámetro -I para especificar la ruta del archivo de encabezado del núcleo En este momento, debe especificar en Makefile.

Orden de búsqueda del archivo de encabezado:
1. La ruta especificada por el parámetro -I (cuando la ruta especificada tiene varias rutas, busque en el orden de la ruta especificada)

2. Luego busque las variables de entorno gcc C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH

3. Busque el directorio predeterminado
/usr/include
/usr/local/include
/usr/lib/gcc-lib/i386-linux/2.95.2/include
/usr/lib/gcc-lib/i386-linux/2.95.2 /../../../../include/g++-3
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux /incluir

Archivos de biblioteca, pero si hay un prefijo dado al instalar gcc, entonces es
/usr/include
prefix/include
prefix/xxx-xxx-xxx-gnulibc/include
prefix/lib/gcc-lib/xxxx-xxx-xxx-gnulibc /2.8.1/incluir

3. Linux especifica la ruta de la biblioteca dinámica

Como todos sabemos, las rutas de búsqueda predeterminadas para las bibliotecas dinámicas de Linux son /lib y /usr/lib. Después de crear la biblioteca dinámica, generalmente se copia en estos dos directorios. Cuando un programa necesita una biblioteca dinámica y la biblioteca dinámica no se ha cargado en la memoria, el sistema buscará automáticamente el archivo de biblioteca dinámica correspondiente en las dos rutas de búsqueda predeterminadas y luego cargará el archivo en la memoria, de modo que el programa Entonces puede usar las funciones en la biblioteca dinámica y otros recursos de la biblioteca dinámica. En Linux, además de la ruta de búsqueda predeterminada, la ruta de búsqueda de la biblioteca dinámica también se puede especificar mediante los siguientes tres métodos.

1. Especifique la ruta de búsqueda de la biblioteca dinámica en el archivo de configuración /etc/ld.so.conf.
Puede especificar la ruta de búsqueda de la biblioteca dinámica editando el archivo de configuración /etc/ld.so.conf, cada línea de este archivo es una ruta de búsqueda de la biblioteca dinámica. Después de editar el archivo cada vez, debe ejecutar el comando ldconfig para que la configuración modificada tenga efecto.

Tome un ejemplo:
Todos los archivos fuente:
Archivo fuente 1: lib_test.c
#include 
void prt()
{ printf("Me encontraste!!!/n"); } Archivo fuente 2: main.c void prt(); int main() { prt(); return 0; } Proceso de operación: Usamos el programa fuente lib_test.c para crear la biblioteca dinámica lib_test.so a través del siguiente comando. # gcc –o lib_test.o -c lib_test.c # gcc -shared -fPIC -o lib_test.so lib_test.o #O una instrucción directa: #gcc –shared –fPIC –o lib_test.so lib_test.c #
















Nota:
El parámetro -fPIC declara que el segmento de código de la biblioteca de enlaces se puede compartir y
el parámetro -shared declara que se compila como una biblioteca compartida. Tenga en cuenta que el nombre de la biblioteca compartida que compilamos esta vez se llama
lib_test.so, que también es una convención de nomenclatura para las bibliotecas compartidas de Linux: el sufijo usa so y el nombre usa el formato libxxxx.

Luego compile main.c con el siguiente comando para generar el programa de destino main.out.
# gcc -o main.out -L.-l_test main.c
#

Tenga en cuenta por qué -l_test?

Luego mueva los archivos de la biblioteca al directorio /root/lib.
# mkdir /root/lib
# mv lib_test.so /root/lib/ lib_test.so
#

Finalmente, edite el archivo de configuración /etc/ld.so.conf y agregue una línea /root/lib al archivo.

Ejecute el programa main.out:
# ./main.out
./main.out: error al cargar bibliotecas compartidas: lib_test.so: no se puede abrir el archivo de objeto compartido: No existe tal archivo o directorio
#
Ocurrió un error, el sistema no encontró la biblioteca dinámica lib_test. Buscando el motivo, resulta que después de editar el archivo de configuración /etc/ld.so.conf, no se ejecutó el comando ldconfig, por lo que la modificación de ahora aún no ha surtido efecto. Intentemos nuevamente después de ejecutar ldconfig.

# ldconfig
# ./main.out
Me encontraste!!!
#El
programa main.out se ejecuta correctamente e imprime el resultado correcto.

2. Especifique la ruta de búsqueda de la biblioteca dinámica a través de la variable de entorno LD_LIBRARY_PATH.
La ruta de búsqueda de la biblioteca dinámica también se puede especificar configurando la variable de entorno LD_LIBRARY_PATH. Cuando se especifican varias rutas de búsqueda de biblioteca dinámica a través de esta variable de entorno, las rutas se separan con dos puntos ":". El método se ilustra mediante el Ejemplo 2 a continuación.

Dé un ejemplo:
esta vez movemos el archivo lib_test.so obtenido arriba a otro lugar, como en /root, y luego configuramos la variable de entorno LD_LIBRARY_PATH para encontrar lib_test.so. El método para configurar la variable de entorno es el siguiente:
# export LD_LIBRARY_PATH=/root
#Luego
ejecuta:
#./main.out
¡¡¡Me encontraste!!!
#Nota
: No es posible configurar la variable de entorno LD_LIBRARY_PATH=/root, debe ser exportado.

3. Especifique la ruta de búsqueda de la biblioteca dinámica del programa al compilar el código objeto.
También puede especificar la ruta de búsqueda de la biblioteca dinámica del programa al compilar el código objeto. -Wl, indicando que los siguientes parámetros serán pasados ​​al programa de enlace ld (porque gcc puede llamar automáticamente a ld). Aquí, el parámetro "-Wl, -rpath" de gcc se usa para especificar
un ejemplo:
esta vez también movemos el archivo lib_test.so obtenido arriba a otro lugar, como en /root/test/lib,
porque necesitamos para especificar la ruta de búsqueda de la biblioteca dinámica del archivo ejecutable al compilar el código objeto, por lo que debe volver a compilar el programa fuente main.c (consulte el procedimiento 2) con el comando gcc para generar el archivo ejecutable main.out.
# gcc -o main.out -L.-l_test -Wl,-rpath,/root/test/lib main.c
#

Resultado de ejecución:
# ./main.out ¡
¡¡Me encontraste!!!
#

El programa ./main.out se ejecuta correctamente y el resultado de salida es exactamente el resultado de ejecución de la función prt en main.c. Por lo tanto, la biblioteca dinámica buscada por el programa main.out es /root/test/lib/lib_test.so.

Con respecto al uso de -Wl, rpath, déjame dar otro ejemplo, no debería ser difícil ver el método de especificar múltiples rutas:
gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/, -rpath ,/usr/lib/,-rpath,/usr/local/lib prueba.c

Lo anterior presenta tres métodos para especificar la ruta de búsqueda de biblioteca dinámica, además de la ruta de búsqueda de biblioteca dinámica predeterminada /lib y /usr/lib, un total de cinco rutas de búsqueda de biblioteca dinámica, entonces, ¿cuál es el orden de su búsqueda? Los lectores pueden usar el siguiente método para experimentar:
(1) Genere 5 lib_test.so con el método presentado anteriormente y colóquelos en 5 carpetas diferentes, lo que requiere que cada lib_test.so tenga una ruta de búsqueda única y preste atención a la salida principal. del programa .out es diferente.
(2) Ejecute main.out, puede ver que está en la ruta de búsqueda, luego elimine lib_test.so en esta ruta y luego vuelva a ejecutarlo. Por analogía, se puede deducir el orden de búsqueda.

Se puede concluir que la secuencia de búsqueda de la ruta de búsqueda de la biblioteca dinámica es:

1. La ruta de búsqueda de la biblioteca dinámica especificada al compilar el código objeto;

2. La ruta de búsqueda de biblioteca dinámica especificada por la variable de entorno LD_LIBRARY_PATH;

3. La ruta de búsqueda de la biblioteca dinámica especificada en el archivo de configuración /etc/ld.so.conf;

4. La ruta de búsqueda de biblioteca dinámica predeterminada /lib;

5. La ruta de búsqueda de biblioteca dinámica predeterminada /usr/lib.

Cuando la ruta de búsqueda de biblioteca dinámica se especifica en los puntos 1, 2 y 3 anteriores, se pueden especificar varias rutas de búsqueda de biblioteca dinámica y la secuencia de búsqueda se basa en la secuencia de las rutas especificadas. Los lectores interesados ​​lo verifican por sí mismos.

Supongo que te gusta

Origin blog.csdn.net/baidu_38493460/article/details/130369208
Recomendado
Clasificación