Linux 如何创建共享库
库是一个文件,其中包含来自填充到单个文件中的各种目标文件的编译代码。它可能包含一组在特定上下文中使用的函数。例如,当要在程序中使用与线程相关的函数时,会使用“pthread”库。
从广义上讲,库(或程序库)可以有两种类型:
- 共享库
- 静态库
在本文中,我们将专门讨论共享库。
共享库
共享库是可以在运行时链接到任何程序的库。它们提供了一种使用可以加载到内存中任何位置的代码的方法。加载后,任何数量的程序都可以使用共享库代码。因此,通过这种方式,程序的大小(使用共享库)和内存占用可以保持在较低水平,因为许多代码以共享库的形式保持通用。
共享库为开发环境提供了模块化,因为可以更改、修改和重新编译库代码,而无需重新编译使用该库的应用程序。例如,对于 pthread 库代码的任何更改,都不需要对使用 pthread 共享库的程序进行更改。可以通过不同的名称访问共享库:
- 链接器使用的名称('lib' 后跟库名称,后跟 '.so' 。例如 libpthread.so)
- 完全限定名称或 soname('lib' 后跟库名称,后跟 '.so',后跟 '.' 和版本号。例如:libpthread.so.1)
- 真实名称('lib' 后接库名,后接 '.so',后接 '.' 和版本号,后接 '.' 和次要编号,后接 '.' 和版本号. 版本号是可选的。例如,libpthread.so.1.1)
当代码中所做的更改使共享库与以前的版本不兼容时,共享库的版本号就会更改。例如,如果一个函数被完全删除,则需要一个新版本的库。
如果代码中的修改不会使共享库与正在使用的先前版本不兼容,则会更改次要编号。例如,一个小错误修复不会破坏现有共享库的兼容性,因此仅更改了次要编号,而版本保持不变。
在文件系统中的放置
文件系统中主要有三个标准位置可以放置库。
- /lib
- /usr/lib
- /usr/local/lib
我们将在这里使用文件系统层次标准(FHS)。根据 FHS 标准,所有在启动时加载并在根文件系统中运行的库都保存在 /lib 中。而系统内部使用的库存储在 /usr/lib 中。这些库并不打算由用户或 shell 脚本直接使用。还有第三个位置 /usr/local/lib(虽然它没有在最新版本的 FHS 中定义)。如果存在,它包含不属于标准分发的所有库。这些非标准库是您下载的库,可能有问题。
使用 ldconfig
创建共享库后,将共享库复制到您希望库所在的目录(例如 /usr/local/lib 或 /usr/lib)。现在,在此目录中运行 ldconfig 命令。
ldconfig 是做什么的?
您还记得我们之前讨论过,共享库的链接器名称是到完全限定的 soname 的符号链接,而完全限定的 soname 又是到真实名称的符号链接。好吧,这个命令完全一样。
当您运行 ELF 可执行文件时,默认情况下首先运行加载程序。加载程序本身是一个共享对象文件 /lib/ld-linux.so.X,其中“X”是版本号。这个加载器依次查找并加载我们程序所依赖的所有共享库。
加载程序为查找库而搜索的所有目录都存储在 /etc/ld.so.conf 中。搜索 /etc/ld.so.conf 文件中指定的所有目录可能很耗时,因此每次运行 ldconfig 命令时,它都会设置所需的符号链接,然后在文件 /etc/ld.so.cache 中创建缓存,其中写入可执行文件所需的所有信息。从缓存中读取信息非常耗时。这里的问题是每次添加或删除共享库时都需要运行 ldconfig 命令。所以在启动时程序使用 /etc/ld.so.cache 来加载它需要的库。
使用非标准库位置
使用非标准库位置时。可以执行以下三个步骤之一:
将路径添加到 /etc/ld.so.conf 文件。该文件包含加载程序在其中搜索库的所有目录的路径。该文件有时可能包含一行,例如:
include /etc/ld.so.conf.d/*.conf
在这种情况下,只需在同一目录中创建一个 conf 文件。您可以使用以下命令直接将目录添加到缓存:
ldconfig -n [non standard directory path containing shared library]
请注意,这是临时更改,系统重新启动后将丢失。更新环境变量 LD_LIBRARY_PATH 以指向包含共享库的目录。Loader 将使用此环境变量中提到的路径来解决依赖关系。
示例(如何创建共享库)
让我们举一个简单的实际例子来看看我们如何创建和使用共享库。以下是我们要放入共享库中的一段代码(shared.c):
#include "shared.h"
unsigned int add(unsigned int a, unsigned int b)
{
printf("\n Inside add()\n");
return (a+b);
}
shared.h 看起来像:
#include<stdio.h>
extern unsigned int add(unsigned int a, unsigned int b);
让我们首先将 shared.c 作为共享库。
1. 运行以下两条命令创建共享库:
gcc -c -Wall -Werror -fPIC shared.c
gcc -shared -o libshared.so shared.o
第一个命令将代码 shared.c 编译为共享库所需的位置无关代码。
第二个命令实际上创建了一个名为“libshared.so”的共享库。
2. 下面是使用共享库函数'add()'的程序代码
#include<stdio.h>
#include"shared.h"
int main(void)
{
unsigned int a = 1;
unsigned int b = 2;
unsigned int result = 0;
result = add(a,b);
printf("\n The result is [%u]\n",result);
return 0;
}
3.接下来,运行以下命令:
gcc -L/home/himanshu/practice/ -Wall main.c -o main -lshared
该命令编译 main.c 代码并告诉 gcc 将代码与共享库 libshared.so 链接(通过使用标志 -l)并告诉共享文件的位置(通过使用标志 -L)。
4. 现在,使用以下命令导出保存新创建的共享库的路径:
export LD_LIBRARY_PATH=/home/himanshu/practice:$LD_LIBRARY_PATH
上述命令将路径导出到环境变量“LD_LIBRARY_PATH”。
5. 现在运行可执行文件'main':
# ./main
Inside add()
The result is [3]
所以我们看到共享库被加载并且里面的 add 函数被执行了。
- 点赞
- 收藏
- 关注作者
评论(0)