Linux 下的动态库和静态库

函数库

为什么要使用函数库

比如我们平常使用比较多的标准库,里边就包含了很多有用的函数,因此使用函数库能够使我们的开发更为省力。同样多种多样的第三方库也会给编程语言带来极大的扩展性。

函数库划分

主要分为动态库和静态库。

静态库特点

  • 静态库会在编译阶段时被完全整合进代码段中,因此生成的可执行文件会比较大
  • 但这种编译后的执行程序不再需要函数库的支持
  • 但同时静态库如果发生了改变,程序必须要重新编译

动态库特点

  • 与静态库不同,动态库在编译阶段不完全被整合进代码段中,因此此时产生的可执行文件比较小
  • 同时因为函数库没有完全被整合进程序当中,程序执行到相关函数时才调用函数库中的对应函数,因此程序的运行环境中必须要提供对应的库
  • 但动态函数库的改变并不会改变程序本身

库的命名规则

  • Linux 中静态库的名字一般是 libxxx.a
  • Linux 中动态库的名字一般是 libxxx.so
  • Windows 中静态库的扩展名一般是 lib
  • Windows 中动态库的扩展名一般是 lib/dll,不过此时的 lib 与静态库中的 lib 有所区别

有时候库的名字是 libxxx.so.major.minor,xxx 是该库的名称,major 是该库的主版本号,minor 是该库的副版本号

库路径

一般来说 Linux 系统中函数库的存放路径为 /lib,/usr/lib,/usr/local/lib。

使用特点

  • 当要使用静态库时,连接器会找到程序所需的函数,然后将之拷贝到执行文件中,由于静态库的特点,因此一般连接成功,静态库就不再需要了
  • 但当要使用动态库时,动态库会在执行程序内留下一个标记,表示当程序执行时,需要首先载入这个库
  • 当静态库和动态库同名时,gcc 命令将优先使用动态库

ldd

ldd 命令能够打印依赖项的信息:

ldd  prints the shared objects (shared libraries) required by each program or shared object specified on the command line.

以之前编译过的一个可执行程序为例,看一下其中的库依赖关系:

wood@ubuntu:/tmp/test$ file list 
list: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=04fdccad327a8c8db231590323544e895a3d446c, not stripped
wood@ubuntu:/tmp/test$ ldd list 
	linux-vdso.so.1 =>  (0x00007ffdf291c000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f38f312b000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f38f34f5000)

使用库进行编译

原函数为:

#include <stdio.h>
#include <math.h>

int main()
{
    double x = 3.14;
    printf("sin(x) = %f\n",sin(x));
    return 0;
}

 结果为:

wood@ubuntu:/tmp/test$ gcc main.c 
/tmp/ccUjDv2w.o: In function `main':
main.c:(.text+0x23): undefined reference to `sin'
collect2: error: ld returned 1 exit status
wood@ubuntu:/tmp/test$ gcc main.c -lm -static -o smain
wood@ubuntu:/tmp/test$ gcc main.c -lm -o dmain
wood@ubuntu:/tmp/test$ ./smain 
sin(x) = 0.001593
wood@ubuntu:/tmp/test$ ./dmain 
sin(x) = 0.001593
wood@ubuntu:/tmp/test$ ll
total 1012
drwxrwxr-x  2 wood wood    4096 Apr 16 22:39 ./
drwxrwxrwt 15 root root    4096 Apr 16 22:39 ../
-rwxrwxr-x  1 wood wood    8656 Apr 16 22:39 dmain*
-rw-rw-r--  1 wood wood     122 Apr 16 22:37 main.c
-rwxrwxr-x  1 wood wood 1007744 Apr 16 22:38 smain*

从结果也可以看出,使用静态库编译出来的可执行程序要比使用动态库编译出来的可执行程序大得多。

动态库和静态库构建

ar

使用命令 ar 能够构建动态库和静态库:

NAME
       ar - create, modify, and extract from archives

SYNOPSIS
       ar [-X32_64] [-]p[mod] [--plugin name] [--target bfdname] [relpos]
       [count] archive [member...]

DESCRIPTION
       The GNU ar program creates, modifies, and extracts from archives.  An
       archive is a single file holding a collection of other files in a
       structure that makes it possible to retrieve the original individual
       files (called members of the archive).

       The original files' contents, mode (permissions), timestamp, owner, and
       group are preserved in the archive, and can be restored on extraction.

       GNU ar can maintain archives whose members have names of any length;
       however, depending on how ar is configured on your system, a limit on
       member-name length may be imposed for compatibility with archive
       formats maintained with other tools.  If it exists, the limit is often
       15 characters (typical of formats related to a.out) or 16 characters
       (typical of formats related to coff).

       ar is considered a binary utility because archives of this sort are
       most often used as libraries holding commonly needed subroutines.

       ar creates an index to the symbols defined in relocatable object
       modules in the archive when you specify the modifier s.  Once created,
       this index is updated in the archive whenever ar makes a change to its
       contents (save for the q update operation).  An archive with such an
       index speeds up linking to the library, and allows routines in the
       library to call each other without regard to their placement in the
       archive.

       You may use nm -s or nm --print-armap to list this index table.  If an
       archive lacks the table, another form of ar called ranlib can be used
       to add just the table.

       GNU ar can optionally create a thin archive, which contains a symbol
       index and references to the original copies of the member files of the
       archive.  This is useful for building libraries for use within a local
       build tree, where the relocatable objects are expected to remain
       available, and copying the contents of each object would only waste
       time and space.

       An archive can either be thin or it can be normal.  It cannot be both
       at the same time.  Once an archive is created its format cannot be
       changed without first deleting it and then creating a new archive in
       its place.

       Thin archives are also flattened, so that adding one thin archive to
       another thin archive does not nest it, as would happen with a normal
       archive.  Instead the elements of the first archive are added
       individually to the second archive.

       The paths to the elements of the archive are stored relative to the
       archive itself.

       GNU ar is designed to be compatible with two different facilities.  You
       can control its activity using command-line options, like the different
       varieties of ar on Unix systems; or, if you specify the single command-
       line option -M, you can control it with a script supplied via standard
       input, like the MRI "librarian" program.

静态库构建

  • 先将 xxx.c 文件生成 xxx.o 文件,命令为:gcc -c xxx.c -o xxx.o
  • 将 xxx.o 文件打包为 libxxx.a 静态库文件,命令为:ar rc libxxx.a xxx.o
  • 将 libxxx.a 放到默认库路径,或者指定链接库路径
wood@ubuntu:/tmp/test$ ls
main.c  node.c  node.h 
wood@ubuntu:/tmp/test$ gcc -c node.c
wood@ubuntu:/tmp/test$ ls
main.c  node.c  node.h  node.o
wood@ubuntu:/tmp/test$ ar rc libnode.a node.o
wood@ubuntu:/tmp/test$ ls
libnode.a  main.c  node.c  node.h  node.o
wood@ubuntu:/tmp/test$ gcc main.c -L. -lnode
wood@ubuntu:/tmp/test$ ls
a.out  libnode.a  main.c  node.c  node.h  node.o
wood@ubuntu:/tmp/test$ ./a.out 
insert a node from tail
...
wood@ubuntu:/tmp/test$ mv libnode.a libnode.a.bak
wood@ubuntu:/tmp/test$ ./a.out 
insert a node from tail
...

动态库构建

  • 先将 xxx.c 文件生成 xxx.o 文件,命令为:gcc -fPIC -c xxx.c -o xxx.o( -fPIC 表示编译为位置独立的代码,用于编译共享库。目标文件需要创建为位置无关码。也就是说可执行程序在加载该库的时候,该库可以位于可执行程序内存中的任何地方)
  • 将 xxx.o 文件打包为 libxxx.so 动态库文件,命令为: gcc -shared -o libxxx.so xxx.o
  • 将 libxxx.so 放到默认库路径,或者指定链接库路径
wood@ubuntu:/tmp/test$ ls
main.c  node.c  node.h
wood@ubuntu:/tmp/test$ gcc -fPIC -c node.c
wood@ubuntu:/tmp/test$ ls
main.c  node.c  node.h  node.o
wood@ubuntu:/tmp/test$ gcc -shared -o libnode.so node.o
wood@ubuntu:/tmp/test$ ls
libnode.so  main.c  node.c  node.h  node.o
wood@ubuntu:/tmp/test$ gcc main.c -L. -lnode
wood@ubuntu:/tmp/test$ ls
a.out  libnode.so  main.c  node.c  node.h  node.o
wood@ubuntu:/tmp/test$ ./a.out 
insert a node from tail
...
wood@ubuntu:/tmp/test$ mv libnode.so libnode.so.bak
wood@ubuntu:/tmp/test$ ls
a.out  libnode.so.bak  main.c  node.c  node.h  node.o
wood@ubuntu:/tmp/test$ ./a.out
./a.out: error while loading shared libraries: libnode.so: cannot open shared object file: No such file or directory

链接路径

其实函数库一般不是放在工作路径使用的,而是放在默认路径或者其他路径然后指定路径进行链接。

链接时如果出现 "xxx undefined" 的错误,说明找不到某个函数,大概率就是链接时找不到对应的动态函数库,链接动态库的路径为:

  • 默认路径:/lib,/usr/lib,/usr/local/lib
  • -L 编译时指定库的位置,-l 编译时指定库的名字
  • 环境变量 LD_LIBRARY_PATH 指定链接路径

运行路径

运行时如果出现 "error while loading shared libraries" 的错误,说明在加载时找不到对应的动态库:

  • 修改配置文件 /etc/ld.so.conf 中指定的动态库搜索路径
  • 环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径
  • 默认的动态库搜索路径 /lib,/usr/lib
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值