Program Libraries

目录

介绍

示例

static library

shared library

Dynamically Loaded Library

FootNote


介绍

Program Libraries主要分为三大类,

static library, shared library 和 dynamically loaded library

那他们三者之间的区别是什么呢,在Program Library HOWTO  该文档上是这样说的,This paper first discusses static libraries, which are installed into a program executable before the program can be run. It then discusses shared libraries, which are loaded at program start-up and shared between programs. Finally, it discusses dynamically loaded (DL) libraries, which can be loaded and used at any time while a program is running. 

为了直观形象地说明,

我们在编译好static library,并把静态链接库链接生成可执行文件program后,把libxxx.a文件删除后,可执行文件program还是可以被执行的,说明生成可执行文件program后,libxxx.a与program没有关系了。也就是说libxxx.a成为program的一部分了。

相反,我们编译好shared library,并把动态链接库链接生成可执行文件program后,把libxxx.so文件删除,可执行文件program会报错,提示找不到libxxx.so文件的。因为在program运行之前,会在默认目录/lib ,/usr/lib ,/usr/local/lib 下先后查找动态链接库libxxx.so文件,如果要把动态链接库放子自己的目录下,需要export下相关路径,

$ export LD_LIBRARY_PATH=/home/:$LD_LIBRARY_PATH

也就是shared library是在程序运行前被动态加载的。

而dynamically loaded library是在程序运行期间,任何时刻都可以被加载的。

示例

static library

编写静态库源文件 hello.c

#include <stdio.h>
void hello()
{
    printf("hello world.");
}

编写静态库头文件 hello.h 

void hello();

gcc编译,加参数-c, 生成object文件 

-C表示只编译不链接,即编译生成object文件,而不链接生成可执行文件

$ gcc -c hello.c -o hello.o

用object文件生成静态链接库文件

$ ar rcs libhello.a hello.o

参数r,在库中插入模块。当插入的模块名已经在库中存在,则替换同名模块。

参数c,创建一个库。不管库是否存在,都将创建。

参数s,创建目标文件索引。

静态链接库一般命名格式是libxxx.a

编写测试程序 test.c

#include "hello.h"
int main()
{
    hello();
    return 0;
}
$ gcc test.c libhello.a -o test
$ ./test
hello world

shared library

我们同样用和上面差不多的例子来演示shared library

编写动态库源文件 hello.c

#include <stdio.h>
void hello(void)
{
    printf("hello world.");
}

编写动态库头文件 hello.h 

void hello();

 gcc编译动态库源文件,生成object文件

$ gcc -fPIC -c hello.c

-fPIC参数是指生成地址无关码的意思,也就是将代码中引用的全是相对地址,而不是绝对地址,真的是这样吗?我在《程序员的自我修养》第7.3节中地址无关码中看到,

那么什么是"-fPIC"呢?使用这个参数会有什么效果呢?

装载时重定位是解决动态模块中有绝对地址引用的办法之一,但是它有一个很大的缺点是指令部分无法在多个进程之间共享,这样就失去了动态链接节省内存的一大优势。我们还需要一种更好的方法解决共享对象指令中绝对地址的重定位问题。其实我们的目的很简单,希望程序模块中共享的指令部分在装载时不需要因为装载地址的改变而改变,所以实现的基本想法就是把指令中的那些需要被修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以在每个进程中拥有一个副本。这种方案就是目前被称为地址无关代码(PIC,Position-independent Code)的技术。

gcc将object文件生成动态库链接库文件

$ gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0.1 hello.o -lc

-shared 表示要生成共享库,必须要加这个参数

-Wl,-soname,libhello.so.1  -Wl是把逗号后面的内容,作为链接器(ld)的参数,是用逗号代替必要的空格。-soname libhello.so.1这个参数给ld的话,是指定生成的so文件的soname.

其中libhello.so.1是shared library的soname,libhello.so.1.0.1是share library的real name。 -o libhello.so.1.0是指实际生成的链接库,而-soname,libhello.so.1是告诉程序执行的时候,装载libhello.so.1,而libhello.so.1是指向libhello.so.1.0.1的,所以实际装载了libhello.so.1.0.1

接下来我们探讨下这些命名:

Share library有关的soname ,real name,link name

那为什么要有以上三个名字呢?因为这是Linux为解决share library的版本问题而使用的特殊机制。下面我就来详细看下这三个命名,

real name顾名思义就是真正的文件,一般soname是real name的符号链接,link name又是soname的符号链接,之所以这样,是和他们的实际用途有关的。

real name是实际生成的库文件,它的命名规则是libxxx.so.X.Y.Z

其中xxx是自定义的库名字,

X是主版本号,一般在库的接口发生改变,无法兼容的时候,更新这个数字,

Y是次版本号,如果接口没有发生改变,只是升级算法,增加新的接口,更新这个数字,

Z是build版本号,一般每升一级,加1,也可以省略。

soname,在共享库生成的时候,一般会指定这个名字,格式为libxxx.so.x

link name也是指向real name的符号链接,它是在使用-lhello参数的时候,就会去寻找link name是libhello.so的库文件,链接生成可执行文件。

$ln -s libhello.so.1.0.1 libhello.so.1
$ln -s libhello.so.1 libhello.so

沿用之前的测试程序 test.c,编译并运行

$ gcc test.c -o test -L. -lhello
$ ./test
./main:error while loading shared libraries:libhello.so.1
:cannot open shared object file
:No such file or directory
$ export LD_LIBRARY_PATH=/home/test/:$LD_LIBRARY_PATH
$ ./test
hello world

Dynamically Loaded Library

Dynamically Loaded Library就是相当于windows下面的DLL.

把上面的libhello.so继续使用,案例如下:

动态加载库测试程序 test.c

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>

int main()
{
    void *handle;
    double (*function)(void);
    char *error;
    handle=dlopen("./libhello.so",RTLD_LAZY);

    /*
       diopen的函数原型:void *dlopen(const char *filename,int flag);
       filename是libhello.so的路径,
       flag分别有以下几种取值,
       RTLD_LAZY    Perform lazy binding. Only resolve symbols as the code that references them is executed. If the symbol is never referenced, then it is never resolved. (Lazy binding is only performed for function references; references to variables are always immediately bound when the library is loaded.)
在dlopen()返回前,不解析未定义的变量的地址
       RTLD_NOW     If this value is specified, or the environment variable LD_BIND_NOW is set to a nonempty string, all undefined symbols in the library are resolved before dlopen() returns. If this cannot be done, an error is returned.
在dlopen()返回前,解析每个未定义变量的地址,如果解析不出来,dlopen会返回NULL
Zero or more of the following values may also be ORed in flag:
       RTLD_GLOBAL  The symbols defined by this library will be made available for symbol resolution of subsequently loaded libraries.
在库中定义的symbols(变量)在随后加载的链接库中变得可以使用。
dlopen()函数的功能是打开一个新库,并将它装入内存,返回的handle作为dlsym()的参数传入,dlsym返回symbol的地址。
     */

    if(!handle)
    {
        fputs(dlerror(),stderr);
        exit(1);
    }

    f=dlsym(handle,"hello");
    /*
      void *dlsym(void *handle,char *symbol);
      handle is the value returned from dlopen,symple is a NIL-terminated string.
      handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数或者全局变量的名称,返回void *类型的值,是指向函数的指针,供调用。
    */

    if((error=dlerror())!=NULL)
    {
        fputs(eoor,stderr);
        exit(1);
    }

    f(); /*调用libhello.so库中的hello()函数,因为dlsym()已经把它的地址传过来了。*/
    dlclose(handle);
}

编译动态加载库的测试程序 

$ gcc test.c -ldl -o test

编译时要加上-ldl参数来与dl library链接

$ ./test
hello world.

关于dl的详细内容请参阅

$man dlopen

FootNote

gcc中库的链接顺序是从右往左进行,所以要把最基础实现的库放在最后,这样左边的lib就可以调用右边的lib中的代码。同时,当一个函数的实现代码在多个lib都存在时,最左边的lib代码最后link,所以也将最终保存下来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值