Linux之静态库,动态库,动态加载库
来源:互联网 发布:手持式网络测试仪 编辑:程序博客网 时间:2024/05/17 23:50
一. 静态库
静态库是.o文件的集合,一般通过ar工具使用-rsv将所有的.o文件打包为libXXXX.a的格式. 在生成可执行文件时,使用ld工具对libXXXX.o进行链接。静态库,只是在程序链接的时候起作用,程序在运行时将脱离静态库。这是因为静态库的代码和数据已经集成到最终的可执行程序中了。其缺点就是最终生成的可执行文件占用的空间比较大,而且当库更新后,程序需要进行重新链接。这增加了程序升级的复杂性。
以下为3个源文件1.c,2.c以及3.c:
[lichao@sg01 a]$ cat 1.c #include<stdio.h>int func1(){ printf("calling func1...\n"); return 0;}[lichao@sg01 a]$ cat 2.c #include<stdio.h>void func2() { printf("calling func2...\n");}[lichao@sg01 a]$ cat main.c #include<stdio.h>int main() { func1(); func2(); return 0;}具体的静态库操作演示如下:
[lichao@sg01 a]$ ls1.c 2.c main.c[lichao@sg01 a]$ gcc -c 1.c 2.c main.c [lichao@sg01 a]$ ls1.c 1.o 2.c 2.o main.c main.o[lichao@sg01 a]$ ar -rsv libprint.a 1.o 2.oar: creating libprint.aa - 1.oa - 2.o[lichao@sg01 a]$ gcc -o main main.o -L. -lprint[lichao@sg01 a]$ ./main calling func1...calling func2...[lichao@sg01 a]$ gcc -o main main.o libprint.a[lichao@sg01 a]$ ./main calling func1...calling func2...在使用静态库生成可执行程序时,有两种方法:
- 如果静态库的名字是约定俗称的以“lib”开头并且以“a”为后缀,则可以用-L指示搜索的位置,用-lname来表示libname.a
- 如果静态库的名字不符合约定俗称的名字规范,则可以直接将静态库的名字写在可执行文件和-L选项的后面
二. 动态库
动态库的目的就是为了消除静态库的这两个缺点。动态库也可以理解为共享库,通常的后缀为so(shared object). 在程序链接的过程中,并不像静态库那样需要把数据和代码拷贝到最终的可执行程序文件中,而只是在程序文件的某个区域中做一些标记。具体的说,链接器在链接的过程中,会在指定的动态库中搜索、解析被主程序调用的函数和引用的数据,如果找到对应的符号信息,则在主程序的XCOFF头结构的loader区域中,建立包含引用的动态库的映射信息。在主程序的执行阶段,系统的相关模块会读取定义在主程序XCOFF头结构loader区域中的相关信息,查找并加载相关的动态库。当所有引用的动态库加载到内存后,程序才开始执行。
动态共享库的名字一般有三种,分别是linker-name, so-name, real-name.以下面的so为例:
lrwxrwxrwx 1 root root 20 2008-05-25 13:54 libncurses.so -> /lib/libncurses.so.5
lrwxrwxrwx 1 root root 13 2008-05-26 15:18 libncurses.so.5 -> libtermcap.so
其中,libncurses.so为linker-name,因为在编译时实际用的名字。libncurses.so.5是so-name,so-name通常还包含主版本号(5),副版本号和发行号.libtermcap.so是real-name,即实际保存数据和代码的库.
在执行一个可执行的二进制ELF文件时,一个特殊的程序“程序装载器”会被自动装载并执行。在Linux中这个装载器就是/lib/ld-linux.so.X(X是版本号). 装载器会首先查找应用程序所依赖的所有的共享库。而这些查找的目录就保存在文件/etc/ld.so.conf文件中.为了加速这个查找和搜索过程,可以使用ldconfig命令,它缺省时读取/etc/ld.so.conf文件,将所有的动态共享库按照一定的规则建立符号链接,然后将这些信息写入/etc/ld.so.cache这个文件中.正是/etc/ld.so.cache这个文件的存在大大加快了程序的启动速度。还可以使用LD_LIBRARY_PATH这个环境变量来设置ld的装载目录,这样装载器就会先搜索LD_LIBRARY_PATH指定的目录,然后再搜索目录的路径. 如果这种方式也不使用的话,可以使用--library-path将搜索的目录传给装载器,具体格式如下:
/lib/ld-linux-so.2 --library-path PATH EXECUTABLE
构成动态共享库(.so)的目标文件(.o)需要使用-fpic参数来生成,典型的用法是:
gcc -fpic -c xxx.c
生成动态共享库的语法格式为:
gcc -shared -Wl,soname,target_so_name.so -o library_so_name library_list
以下三个文件为实验的源文件:1.c 2.c 3.c
[lichao@sg01 so]$ ls -llrttotal 12-rw-rw-r-- 1 lichao lichao 65 Mar 28 21:27 main.c-rw-rw-r-- 1 lichao lichao 67 Mar 28 21:27 2.c-rw-rw-r-- 1 lichao lichao 76 Mar 28 21:27 1.c[lichao@sg01 so]$ gcc -fpic -c 1.c 2.c [lichao@sg01 so]$ gcc -c main.c [lichao@sg01 so]$ gcc -shared -Wl,-soname,libprint.so -o libprint.so.0.0.1 1.o 2.o [lichao@sg01 so]$ ls1.c 1.o 2.c 2.o libprint.so.0.0.1 main.c main.o[lichao@sg01 so]$ gcc -o main main.o libprint.so.0.0.1 [lichao@sg01 so]$ ./main ./main: error while loading shared libraries: libprint.so: cannot open shared object file: No such file or directory[lichao@sg01 so]$ su rootPassword: [root@sg01 so]# cp libprint.so.0.0.1 /usr/local/lib[root@sg01 so]# exitexit[lichao@sg01 so]$ ./main ./main: error while loading shared libraries: libprint.so: cannot open shared object file: No such file or directory单纯这样将SO文件拷贝到库中是不行的,因为库搜索的时候是按照libprint.so去搜索的。需要在这里再建立一个链接到libprint.so.0.0.1的符号链接文件。操作如下:
[lichao@sg01 so]$ su rootPassword: [root@sg01 so]# cd /usr/local/lib[root@sg01 lib]# ln -s libprint.so.0.0.1 libprint.so[root@sg01 lib]# ls -llrttotal 142212....-rwxr-xr-x 1 root root 6067 Mar 28 21:49 libprint.so.0.0.1lrwxrwxrwx 1 root root 17 Mar 28 21:51 libprint.so -> libprint.so.0.0.1[root@sg01 lib]# exitexit[lichao@sg01 so]$ ./main calling func1...calling func2...这样就可以了,如果不用做版本维护的话,可以使用更简单的操作:
[lichao@sg01 so]$ ls1.c 2.c main.c[lichao@sg01 so]$ gcc -fpic -shared -o libprint.so 1.c 2.c [lichao@sg01 so]$ ls1.c 2.c libprint.so main.c[lichao@sg01 so]$ su root Password: [root@sg01 so]# cp libprint.so /usr/local/lib[root@sg01 so]# exitexit[lichao@sg01 so]$ gcc -o main main.c -L. -lprint[lichao@sg01 so]$ ./main calling func1...calling func2...
三. 动态加载库
函数原型:void *dlopen(const char *libname,int flag);
功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失
a.根据环境变量LD_LIBRARY_PATH查找
b.根据/etc/ld.so.cache查找
c.查找依次在/lib和/usr/lib目录查找。
flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数
(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之后,析构函数会被调用。
[lichao@sg01 dynamic_load]$ cat 1.c#include<stdio.h>int func1(){ printf("calling func1...\n"); return 0;}[lichao@sg01 dynamic_load]$ cat 2.c#include<stdio.h>void func2() { printf("calling func2...\n");}[lichao@sg01 dynamic_load]$ cat main.c #include <stdio.h>#include <dlfcn.h>#include <stdlib.h>int main() { void * handle; void (*func)(); handle=dlopen("/usr/local/lib/libmyso.so",RTLD_NOW); if(handle == NULL) { fprintf(stdout,dlerror()); exit(-1); } func=dlsym(handle,"func1"); if(func == NULL){ fprintf(stdout,dlerror()); exit(-2); } func(); func=dlsym(handle,"func2"); if(func == NULL) { fprintf(stdout,dlerror()); exit(-3); } func(); return 0;}演示结果如下:
[lichao@sg01 dynamic_load]$ ls1.c 2.c main.c[lichao@sg01 dynamic_load]$ gcc -fpic -shared -o libmyso.so 1.c 2.c[lichao@sg01 dynamic_load]$ ls1.c 2.c libmyso.so main.c[lichao@sg01 dynamic_load]$ su rootPassword: [root@sg01 dynamic_load]# cp libmyso.so /usr/local/libcp: overwrite `/usr/local/lib/libmyso.so'? y[root@sg01 dynamic_load]# exitexit[lichao@sg01 dynamic_load]$ gcc -o main main.c -ldl[lichao@sg01 dynamic_load]$ ./main calling func1...calling func2...
- Linux之静态库,动态库,动态加载库
- Linux动态库、静态库加载基础
- Linux动态库、静态库加载基础
- Linux动态库、静态库加载基础
- Linux静态链接库,动态链接库,动态加载库
- linux 动态加载动态库
- linux 动态加载动态库
- linux 动态加载动态库
- Linux 动态/静态库
- 动态库DLL加载方式-静态加载和动态加载
- 动态库DLL加载方式-静态加载和动态加载
- 动态库DLL加载方式-静态加载和动态加载
- linux之动态库与静态库
- linux之动态库和静态库
- 动态库调用方式 动态加载 静态加载
- 动态链接库dll的 静态加载 与 动态加载
- 动态链接库dll的 静态加载 与 动态加载
- 动态库的动态加载和静态加载
- strtr和str_replace效率探索
- VS2010 常用快捷键
- IOS开发 通过NSDateComponents和NSCalendar定义NSDate
- opencv CvSeq学习
- XFree86-4.6.0全编译
- Linux之静态库,动态库,动态加载库
- 移动应用设计中应避免的十种错误
- android browser 的几个小feature (五) Android Browser带网络认证的下载实现
- Android SimpleAdapter 的list刷新问题。
- 如何用 Pytbull 测试 suricata?
- statusBar设置
- Uncaught SyntaxError: Unexpected token ILLEGAL
- linux下串口编程设置函数---------set_opt(fd1,115200,8,'N'1)--------------------
- 模板模式-类行为型模式