Linux 如何创建共享库

举报
Tiamo_T 发表于 2022/06/22 17:27:23 2022/06/22
【摘要】 在本文中,我们将专门讨论共享库。

库是一个文件,其中包含来自填充到单个文件中的各种目标文件的编译代码。它可能包含一组在特定上下文中使用的函数。例如,当要在程序中使用与线程相关的函数时,会使用“pthread”库。

从广义上讲,库(或程序库)可以有两种类型:

  1. 共享库
  2. 静态库

在本文中,我们将专门讨论共享库。

共享库

共享库是可以在运行时链接到任何程序的库。它们提供了一种使用可以加载到内存中任何位置的代码的方法。加载后,任何数量的程序都可以使用共享库代码。因此,通过这种方式,程序的大小(使用共享库)和内存占用可以保持在较低水平,因为许多代码以共享库的形式保持通用。

共享库为开发环境提供了模块化,因为可以更改、修改和重新编译库代码,而无需重新编译使用该库的应用程序。例如,对于 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 函数被执行了。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。