在Linux上用C语言创建静态和动态链接库

本文深入探讨了C程序中的静态链接和动态链接库。静态链接将所有库函数复制到可执行文件中,占用更多磁盘和内存空间,但运行速度快且易于移植。动态链接则在运行时加载库,多个程序可以共享同一库,节省空间。创建静态库涉及将对象文件打包成.a文件,而动态库(.so文件)创建需要-fpic选项以生成位置独立代码,并通过ldconfig命令安装。动态链接利用共享库,可执行文件更小,但依赖于系统库。
摘要由CSDN通过智能技术生成

静态和动态链接库

    静态链接和动态链接是收集和组合多个对象文件以创建单个可执行文件的两个过程。链接既可以在编译时执行,也可以当源代码转换并加载机器代码时执行,也可以当程序被加载到内存并由加载器执行时执行,甚至可以在运行时由应用程序执行。同时,它由被称为链接器的程序执行。链接器也称为链接编辑器,链接是编译程序的最后一步。在本教程中,将讨论C模块的静态和动态链接。

什么是链接器?

    链接器是系统软件,因为它可以进行单独编译,因此在软件开发中起着至关重要的作用。您可以将大型应用程序分解为更小、更易于管理的块,这些块可以单独修改和编译,而不是将其组织为一个单一的源文件。当您更改其中一个模块时,只需重新编译它并重新链接应用程序,而无需重新编译其他源文件。
    在静态链接期间,链接器将程序中使用的所有库例程复制到可执行映像中。这当然比动态链接占用更多的磁盘和内存空间。但是静态链接速度更快,更便于移植,因为当它运行时,系统上不需要存在该库。
    另一方面,在动态链接中,共享库的名称放置在可执行文件映像中,而实际链接是在运行时将可执行文件和库都放置在内存中进行的。动态链接具有在多个程序之间共享单个可共享库的优势。
    链接器作为一个系统程序,采用可重定位的对象文件和命令行参数来生成可执行的对象文件。要生成可执行文件,链接器必须执行符号解析和重定位。
    注意:对象文件有三种类型,即可重定位、可执行和共享。可重定位对象文件包含代码和数据,可以在编译时与其他同类对象文件组合以创建可执行对象文件。它由各种代码和数据部分组成,指令在一节,初始化的全局变量在另一节,未初始化的变量在另一节。可执行对象文件包含二进制代码和数据,可以直接复制到内存中并执行。共享对象文件是可以加载到内存中并由链接器在加载或运行时动态链接的文件。
    本文将对静态链接和动态链接进行说明。链接时,链接器会抱怨缺少函数定义(如果有的话)。在编译过程中,如果编译器没有找到特定模块的函数定义,它只会假设该函数是在另一个文件中定义的,并将其视为外部引用。编译器一次不会查看多个文件。然而,链接器可能会查看多个文件,并为未提及的模块寻找引用。独立的编译和链接过程降低了程序的复杂度,并使代码易于分解成更小的片段,从而更好地管理。

什么是静态链接?

    静态链接是将程序中使用的所有库模块复制到最终可执行映像中的过程。这是由链接器执行的,它是编译过程的最后一步。链接器将库例程与程序代码相结合,以便解析外部引用,并生成适合加载到内存中的可执行映像。
    让我们看看静态链接的例子。在这里,我们将以一个非常简单的例子来演示静态链接过程,即添加两个整数。我们将开发一个add模块并将其放在一个单独的add.c文件中。add模块的原型将放在一个名为add.h的单独文件中。代码文件addDemo.c将被创建来演示链接过程。
    首先,创建一个头文件add.h并将add函数声明插入其中,如下所示:

int add(int, int);

    现在,创建另一个源代码文件 addDemo.c,并将以下代码插入其中:

#include <add.h>
#include <stdio.h>
 
int main()
{
  int x= 10, y = 20;
  printf("\n%d + %d = %d", x, y, add(x, y));
  return 0;
}

    再创建一个名为add.c的文件,其中包含add模块的代码。在add.c中插入以下代码:

int add(int quant1, int quant2)
{
  return(quant1 + quant2);
}

    创建完上述文件后,您可以按照以下步骤开始构建可执行文件:

[root@host ~]# gcc -I . -c addDemo.c

    -I 选项告诉GCC在它后面指定的目录中搜索头文件。这里,GCC被要求在当前目录和include目录中查找头文件。(在类Unix系统中,点(.)被解释为当前目录)。
注意:有些应用程序使用安装在/usr/local/include中的头文件,在编译它们时,它们通常会告诉GCC在那里查找这些头文件。
    -c 选项告诉GCC编译成一个对象文件。对象文件的名称为 *.o。其中,* 是不带扩展名的文件名。之后它将停止,并且不会执行创建可执行文件的链接。
与上面的命令类似,编译add.c来创建对象文件。这是通过以下命令完成的:

[root@host ~]# gcc -c add.c

    这次省略了 -I 选项,因为add.c不需要包含头文件。上面的命令将生成一个新文件,即add.o。现在最后一步是通过将add.o和addDemo.o链接在一起来生成可执行文件。执行以下命令以生成可执行对象文件。

[root@host ~]# gcc -o addDemo add.o addDemo.o

    虽然可以使用 ld 命令直接调用链接器,但是我们更喜欢通过编译器来调用,因为还有其他的对象文件或路径或选项,必须链接这些文件才能得到最终的可执行文件。
    现在我们知道了如何从多个二进制对象文件创建可执行对象文件。是时候了解库了。在下一节中,我们将看到软件开发过程中有哪些库?它们是如何产生的?它们在软件开发中的意义是什么?它们是如何使用的,以及在软件开发中,有许多使库的使用变得显而易见的事情?

如何创建静态库?

    静态库和共享库只是二进制对象文件的集合,它们在链接过程中会提供帮助。一个库包含数百或数千个对象文件。在addDemo的演示过程中,我们有两个对象文件,即add.o和addDemo.o。为了得到最终的可执行对象文件,您可能必须将10个或更多的对象文件链接在一起。在这些情况下,您会发现自己很疲惫,每次都必须列出一个冗长的对象文件列表,以便获得最终的可执行对象文件。此外,零散的对象文件将很难管理。库解决了所有这些困难,并帮助您保持对象文件的组织简单和可维护。
    静态库是可重定位对象文件的捆绑包。通常他们有 .a 扩展名。为了演示静态库的使用,我们将进一步扩展addDemo示例。到目前为止,我们有add.o,它包含我们在addDemo中使用的add函数的二进制目标代码。为了更详细地演示库的使用,我们将创建一个新的头文件heymath.h,并将添加两个函数add,sub的声明。

int add(int, int); //adds two integers
int sub(int, int); //subtracts second integer from first

    接下来,创建一个sub.c文件,并向其中添加以下代码。我们已经创建了add.c:

int sub(int quant1, int quant2)
{
  return (quant1 - quant2);
}

    现在编译sub.c如下,以获得二进制对象文件:

[root@host ~]# gcc -c sub.c

    上面的命令将生成二进制对象文件sub.o。
    现在,我们有两个二进制对象文件,即add.o和sub.o。我们在工作目录中有add.o文件,正如我们在前面的示例中创建的那样。我们现在将通过集合这两个文件来创建一个静态库。它将使我们创建最终可执行对象文件更容易,下一次我们将不必指定两个对象文件连同addDemo,以生成最终的可执行对象文件。通过执行以下命令创建静态库libheymath:

[root@host ~]# ar rs libheymath.a add.o sub.o

    上面的命令生成一个新文件libheymath.a,它是一个包含两个对象文件的静态库,当我们希望在程序中使用add、sub函数或同时使用两者时,可以进一步使用它。
    要使用addDemo中的子函数,我们需要在addDemo.c中做一些更改,然后重新编译它。在addDemo.c中进行以下更改:

#include <heymath.h>
#include <stdio.h>
 
int main()
{
  int x = 10, y = 20;
  printf("\n%d + %d = %d", x, y, add(x, y));
  printf("\n%d + %d = %d", x, y, sub(x, y));
  return 0

    如果您看到,我们已经用 include <heymath.h> 替换了第一个语句 include <add.h> 。因为heymath.h现在包含add和sub函数的声明,并添加了一个printf语句,该语句调用sub函数来打印变量x和y的差。
    现在从工作目录中删除所有的 .o 文件。创建addDemo.o如下:

[root@host ~]# gcc -I . -c addDemo.c

    并将其与heymath.a链接以生成最终的可执行对象文件。

[root@host ~]# gcc -o addDemo addDemo.o libheymath.a

    您还可以使用以下命令作为替代,将libheymath.a与addDemo.o链接起来,以生成最终的可执行文件:

[root@host ~]# gcc -o addDemo -L . addDemo.o -lheymath

    在上面的命令中,-lheymath 应该读作 -l heymath,它告诉链接器将库 <库名>.a 中包含的对象文件链接到addDemo,以生成可执行的对象文件。在我们的例子中,是libheymath.a。
    -L 选项告诉链接器在下面的参数中搜索库(类似于 -I)。所以,我们现在创建的是一个静态库。但这并不是终点;系统也使用了很多动态库。现在正是讨论这些问题的时候。

什么是动态链接?

    动态链接将大部分链接过程推迟到程序开始运行。当程序在系统中执行时,它“动态”执行链接过程。在动态链接过程中,共享库的名称放在最终的可执行文件中,而实际的链接发生在运行时,此时可执行文件和库都放在内存中。使用动态链接库的主要优点是可执行程序的大小大大减小,因为每个程序不必存储它所使用的库函数的冗余副本。另外,当DLL函数被更新时,使用它们的程序将自动得到函数的好处。

如何创建和使用共享库?

    共享库(Linux上)或动态链接库(Windows上的dll)是对象文件的集合。在动态链接中,对象文件不会在编译时与程序合并,也不会永久复制到最终可执行文件中。因此,共享库减小了最终可执行文件的大小。
    共享库或动态链接库(DLL)的一大优点是在多个程序之间共享库的一个副本,因此它们被称为共享库,而将它们与多个程序链接的过程被称为动态链接。
    共享库在启动时由程序加载到内存中。正确加载共享库后,以后启动的所有程序都会自动使用已加载的共享库。下面的文本将演示如何在Linux上创建和使用共享库。
    让我们继续前面的add和sub模块示例。如您所知,我们有两个对象文件add.o和sub.o(从add.c和sub.c编译),分别包含add和sub函数的代码,但是我们必须用 -fpic 或 -fPIC 选项重新编译add.c和sub.c。-fPIC 或 -fpic 选项支持“位置独立代码”生成,这是共享库的要求。-fPIC选择始终有效,但可能会产生比 -fpic 更大的代码。使用 -fpic 选项通常会生成更小更快的代码,但会有平台相关的限制,例如全局可见符号的数量或代码的大小。当您创建共享库时,链接器将告诉您它是否合适。当有疑问时,我选择 -fPIC,因为它总是有效的。因此,在创建共享库时,必须使用以下选项重新编译add.c和sub.c:

[root@host ~]# gcc -Wall -fPIC -c add.c
[root@host ~]# gcc -Wall -fPIC -c sub.c

    上面的命令将在当前目录中生成两个新的对象文件add.o和sub.o。警告选项 -Wall 对许多常见错误启用警告,并且应该始终使用。它结合了许多其他更具体的警告选项,这些选项也可以单独选择。有关详细信息,请参见有关指定警告的手册页。
    现在,使用以下命令构建库libheymath.so。

[root@host ~]# gcc -shared -o libheymath.so add.o sub.o

    这个新创建的共享库libheymath.so可以用作libheymath.a的替代品,但使用共享库并不像静态库那样简单。一旦创建了共享库,就必须安装它。最简单的安装方法是将库复制到一个标准目录(例如,/usr/lib)并运行ldconfig命令。
    然后执行ldconfig命令,addDemo可执行文件可以如下构建,我还重新编译了addDemo.c。如果addDemo.o已经存在于您的工作目录中,您可以省略它。

[root@host ~]# gcc -c addDemo.c
[root@host ~]# gcc -o addDemo addDemo.o libheymath.so

    或者

[root@host ~]# gcc -o addDemo addDemo.o -lheymath

    您可以列出可执行文件所依赖的共享库依赖项。ldd <name of executable>命令可以帮您完成这项工作。

[root@host ~]# ldd addDemo
        linux-vdso.so.1 (0x00007fffdc7e0000)
        libheymath.so => /lib/libheymath.so (0x00007f5601d52000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f560198e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f5601f54000)
参考文档

[1]Krishan Kumar.Create Static and Dynamic Link Libraries in C on Linux[EB/OL].https://cs-fundamentals.com/c-programming/static-and-dynamic-linking-in-c.php,2016-01-01.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值