首页 > 系统服务 > 详细

Linux静态对象库和共享对象库

时间:2017-03-12 19:35:13      阅读:275      评论:0      收藏:0      [点我收藏+]

1 什么是库?
库,就是是源程序文件编译后生成的目标文件


2 为什么要有库?
一个项目中有很多.c文件,比如一个函数可能就是一个源文件,对这样的.c编译后会有很多.o文件,不方便发布,可以把这些.o做到一个起形成库文件,这样别人就可以链接这个库进行使用。

3 库在linux中分两种:

静态对象库(静态库)

库文件以lib开头,以.a结束,中间是库名。

静态库在程序编译时会被连接到目标代码中,目标程序运行时将不再需要该动态库,移植方便,体积较大,但是浪费空间和资源,因为所有相关的对象文件与牵涉到的库被链接合成一个可执行文件。

共享对象库(windows下称为动态链接库)

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此体积较小,可以实现进程间的资源共享,甚至可以真正做到链接载入完全由程序员在程序代码中控制,另外将一些程序的升级变得简单,但是在程序运行时需要动态库存在

 

4 创建静态库的过程

1. 写源程序 xxx.c

比如在当前目录下创建一个lib文件夹,然后写个打印函数

cat print.c

cat print.c
#include <stdio.h> void print(double d){ printf("d=%.2lf\n", d); }

和一个加减法的函数

cat mymath.c

double add(double a, double b){
    return a+b;
}

double sub(double a, double b){
    return a-b;
}

2. 编译源程序, 生成.o 文件

 gcc -c print.c
 gcc -c mymath.c

3. 使用ar打包工具生成静态库

            ar –r libyyy.a xxx1.o xxx2.o …. 

注:

ar -- create and maintain library archives

-r -- Replace or add the specified files to the archive.

其中yyy是库名, xxx1.o,xxx2.o...是要归档的目标文件名

我们对上面的两个.o生成的目标文件创建静态库

ar -r libmymath.a mymath.o print.o
ar: creating archive libmymath.a

4. 提供头文件,方便调用(一般情况下提供一个库文件的同时会提供一个头文件,因为调用库里面某个函数时要声明,一般把这些函数的声明放在一个头文件中,方便别人调用)

#ifndef MYMATH_H
#define MYMATH_H
//add
double add(double a, double b);
//sub
double sub(double a, double b);
//print
void print(double d);
#endif

 

这时候一个静态库就创建完成了,现在看下如何使用这个静态库文件

 使用静态库的过程

1. 写调用源程序xxx.c  (需要包含所用到库的头文件)

 cat mytest.c

#include "mymath.h"
int main(){
    double r = add(10, 20);
    print(r);
    r = sub(3.14, 2.08);
    print(r);
    return 0;

}

2. 编译源程序,生成.o文件

gcc -c mytest.c

3. 链接静态库

gcc xxx.o –l xxx –L path

-l 后面是要连接的库名(不是文件名,是去掉lib 和.a)

-L 是库文件所在的路径 (如果提供环境变量LIBRARY_PATH,可以省略-L选项)

这里我们利用来链接

gcc mytest.o -l mymath -L .

 直接运行得到

./a.out
d=30.00
d=1.06

5 共享对象库

创建共享库的过程如下:

1. 写源程序 xxx.c

2. 编译源程序,用-fpic生成.o 文件

gcc -c -fpic xxx.c

3.  生成共享库文件

gcc --shared xxx.o -o libxxx.so

(用gcc,而不是生成静态库的ar, 注意共享库的库名以lib开头,以.so结尾)

4. 提供头文件,方便调用

使用共享库的过程:

1. 写调用源程序 xxx.c

2.  编译源程序,生成.o 文件

3. 连接共享库文件

 gcc xxx.o -l xxx -L path

如果提供环境变量LIBRARY_PATH, 可以省略-L选项

4 执行过程中,动态加载共享库,系统会自动查找LD_LIBRARY_PATH环境变量中的路径,以确定库文件的位置,如果找到,会把它加载到内存中执行,如果找不到,程序会执行失败, 这点和静态库是不一样的。

 

下面还以上面创建静态库的几个函数为例,介绍共享库的创建和使用

创建共享库

1 把mymath.c print.c mytest.c mymath.h拷贝到slib目录

2  gcc -c -fpic mymath.c print.c   //编译

3 gcc -shared mymath.o print.o -o libmymath.so //生成共享库,这时发现在当前目录有一个以绿色显示的,有执行权限的libmymath.so的库文件

使用共享库:

1 拷贝源文件mytest.c

2 gcc -c mytest.c

3 gcc mytest.o -l mymath -L .

4 a.out

如果执行出错,

a.out: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory

原因可能是LD_LIBRARY_PATH 中没有当前目录,这时需要配置一下就好了

export LD_LIBRARY_PATH=.  (根据实际情况来配置)

./a.out
d=30.00
d=1.06

 

linux上用ldd a.out 可以看到a.out在运行时需要的共享库等资源, 在mac中可以用otools来查看,例如

otool -L a.out
a.out:
    libmymath.so (compatibility version 0.0.0, current version 0.0.0)   
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.0.0)

6 动态加载库

动态库也是在程序运行时载入,但与共享库不同的是,动态库使用的库函数不是在程序运行使开始载入,而是在程序中的语句需要使用该函数时才载入动态库可以在程序运行期间释放动态库所占用的内存,腾出空间供其他程序使用。

有专门的一组API用于完成打开动态库,查找符号,处理出错,关闭动态库等功能。

下面对这些接口函数逐一介绍,用这几个函数需要: #include <dlfcn.h>
   (1) dlopen   
   函数原型:void *dlopen(const char *libname,int flag);
   功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。
   如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。
   参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:
       a.根据环境变量LD_LIBRARY_PATH查找
       b.根据/etc/ld.so.cache查找
       c.查找依次在/lib和/usr/lib目录查找。
   flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。

   (2) dlerror
   函数原型:char *dlerror(void);
   功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。

   (3) dlsym
   函数原型:void *dlsym(void *handle,const char *symbol);
   功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。
   如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,

   (4) dlclose
   函数原型:int dlclose(void *);
   功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。 

还以上面共享库的例子来说明怎么使用动态库。

在slib中有一个libmymath.so, 现在创建一个testdll.c来动态加载使用它。

cat testdll.c

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

int main(){
    // 1.open
    void* handle = dlopen("./slib/libmymath.so", RTLD_LAZY);
    // 2.error?
    char* error = dlerror();
    if(error){
        printf("Open library failed:%s\n", error);
        return -1;
    }
    // 3.get function
    double (*f)(double, double) = dlsym(handle, "add");
    // 4.invoke function
    double r = f(1.23, 2.34);
    printf("r=%.2lf\n", r);
    // 5.close
    dlclose(handle);
    return 0;
}

编译连接:

gcc testdll.c -l dl

dlsym, dlopen 等几个函数定义在libdl.so中,如果不指定-l dl,会出错 "undefined reference to ‘dlopen‘ "

 

部分参考: http://www.cnblogs.com/ThinkingWorld/articles/1861249.html

 

Linux静态对象库和共享对象库

原文:http://www.cnblogs.com/happyorange/p/6538268.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!