静态连接库linux
前言
静态库是obj文件的一个集合(目标文件中一般仅解析了文件内部的变量和函数,对于引用的函数和变量尚未解析,这须要将其余已经编写好的目标文件引用进来,将没有解析的函数和变量进行解析,一般引用的目标是库),一般静态库以".a"为后缀,名字格式通常为libxxx.a。静态库由程序ar生成。缓存
实例程序以下:函数
Main.c工具
#include <stdio.h> extern void print_hello(); int main(void) { print_hello(); }
Print_hello.cpost
#include <stdio.h> void print_hello() { printf("hello\n"); }
-
生成静态连接库
建立静态库的步骤:测试
- 生成目标文件。(使用命令gcc –c file.c)
- 使用工具ar对目标文件进行归档。(使用的命令以下)
生成静态连接库,或者将一个obj文件加入到已经存在的静态库的命令格式为:spa
ar –rcs 库文件obj_1 obj_2 …3d
使用上面的实例程序print_hello.c建立静态连接库:指针
-
使用静态连接库
使用方式一:
使用方式二:
注意,在方法二中"-L./"不可少,不然出现以下错误:
这是由于上面的命令在系统默认的路径下查找hello函数库,而咱们并无将libhello.a库放在系统默认搜索路径下,因此须要显示指定库函数的路径为当前目录。
另外还需注意,在使用-l选项时,-o选项的目标名称要在-l连接的库名称以前,不然gcc会认为-l是生成的目标而出错。
下面两个图分别是头文件和库文件的默认搜索路径:
动态连接库
前言
动态连接库是程序运行时加载的库,当动态连接库正确安装后,全部的 程序均可以使用动态库来运行程序。动态连接库是目标文件的集合,目标文件在动态连接库中的组织方式是按照特殊方式造成的。库中函数和变量的地址是相对地址,不是绝对地址,其真实地址在调用动态库的程序加载时造成。
动态连接库的名称有别名(soname)、真名(realname)和连接名(linker name):
别名:libxxx.so,这种形式的库名正是执行编译命令时编译器要搜索的名字。
真名:动态连接库的真实名称,通常老是在别名的基础上加上一个小版本号、发布版本等构成。
连接名:程序连接时使用的库的名字。
-
生成动态连接库
生成动态连接库的命令很简单,使用-fPIC选项或者-fpic选项。-fPIC和-fpic选项的做用是使得gcc生成的代码是位置无关的,例以下面的命令将print_hello.c编译生成动态连接库:
其中-shared选项告诉编译器生成一个动态连接库;-soname,libhello.so表示生成动态库的别名是libhello.so;-o libhello.so.1选项择表示生成名字为libhello.so.1的实际动态连接库文件。
生成动态连接库以后一个很重要的问题就是安装,通常状况下将生成的动态连接库复制到系统默认的动态连接库的搜索路径下,一般有/lib,/usr/lib,/usr/local/lib,放到其中任何一个目录下均可以。
-
动态连接库的配置
动态连接库并非能够随意地使用,要在运行的 程序中使用动态连接库,须要指定系统的动态连接库搜索的路径,让系统找到运行所须要的动态连接库才能够。
系统中的配置文件/etc/ld.so.conf是动态连接库的搜索路径配置文件。这这个文件内,存放着可被Linux共享的动态连接库所在目录的名字(系统目录/lib,/usr/lib除外,这两个目录默认就是动态连接库的搜索路径),多个目录名间以空白字符(空格、换行等)或冒号或逗号分割。查看系统中的动态连接库配置文件的内容:
Linux的配置文件将目录ld.so.conf.d/中的配置文件包含了进来。
-
动态连接库管理命令
为了让新增长的动态连接库可以被系统共享,须要运行动态连接库的 管理命令ldconfig。ldconfig命令的做用是在系统的默认搜索路径和动态连接库配置文件中所列出的目录里搜索动态连接库,建立动态连接库装入程序须要的连接和缓存文件。搜索完毕后,将结果写入缓存文件/etc/ld.so.cache中,文件中保存的是已经排好序的动态连接库名字列表。
ldconfig命令的用法以下:
-
使用动态连接库
把当前工做目录(libhello.so.1所在的目录)加入动态连接库的搜索路径配置文件/etc/ld.so.conf中:
执行ldconfig命令刷新缓存文件/etc/ld.so.cache:
咱们会发现,执行ldconfig命令后,当前目录下生成了一个新的文件libhello.so(这是-soname,libhello.so选项致使的,若是没有该选项,那么ldconfig执行后不会生成新文件libhello.so。),使用ls命令会发现此文件是libhello.so.1的连接文件:
在编译程序时,使用动态连接库和静态连接库是一致的,使用"-lxxx"的方式:
编译后执行时出现以下错误:
其主要缘由是使用了SELINUX。将其状态设置为disabled便可,方法以下:
打开/etc/selinux/config,将selinux=enforcing或permissive改为disabled。而后存盘退出,重启系统。再次执行,结果以下:
运行时,还可能出现这样的错误:test: error while loading shared libraries: libxxx.so: cannot open shared object file: No such file or directory,这是因为程序运行时没有找到动态连接库形成的。程序编译时连接动态连接库和运行时使用动态连接库的概念是不一样的,在运行时,程序连接的动态连接库须要在系统目录下才行。有几种办法能够解决此种问题:
1)将动态连接库的目录放到程序搜索路径中,能够将库的路径添加到环境变量LD_LIBRARY_PATH中实现:
2)使用ld-Linux.so.2来加载程序,命令格式为:
/lib/ ld-Linux.so.2 –library-path 路径 程序名
加载test程序的命令为:
不过貌似并非全部系统上都有ld-Linux.so.2。
注意,若是系统的搜索路径下同时存在静态连接库和动态连接库,默认状况下会连接动态连接库。若是须要强制连接静态连接库,须要加上"-satic"选项。
动态加载库
前言
动态加载库和通常的动态连接库所不一样的是,通常动态连接库在程序启动时就要寻找动态库,找到库函数;而动态加载库能够用程序的方法来控制何时加载。动态加载库主要有函数dlopen()、dlerror()、dlsym()和dlclose()来控制动态库的使用。函数原型以下:
-
打开动态库dlopen()
函数dlopen()按照用户指定的方式打开动态连接库,其中参数filename为动态连接库的文件名,flag为打开方式,通常为RTLD_LASY,函数的返回值为库的指针。
例如,下面的代码使用dlopen打开当前目录下的动态库libhello.so:
void *phandle = dlopen("./libhello.so", RTLD_LAZY);
-
得到函数指针dlsym()
使用动态连接库的目的是调用其中的函数,完成特定的功能。函数dlsym()能够得到动态连接库中指定函数的指针,而后可使用这个函数指针进行操做。其中参数handle为dlopen()打开动态库后返回的句柄,参数symbol为函数的名称,返回值为函数指针。
-
使用动态加载库实例
#include <stdio.h> #include <dlfcn.h> int main(void) { void (*printhello)(void); void *phandle = NULL; char *perr = NULL;
phandle = dlopen("./libhello.so", RTLD_LAZY); if(!phandle) { printf("Failed load library!\n"); } perr = dlerror(); if(perr != NULL) { printf("%s\n", perr); return(0); } printhello = dlsym(phandle, "print_hello"); perr = dlerror(); if(perr != NULL) { printf("%s\n", perr); return(0); } (*printhello)(); dlclose(phandle);
return(0); }编译运行:
注意,想要使用dlfcn.h中的函数,编译时必须加上选项-ldl。
小发现:
有没有注意到,咱们并无使用用到include <hello.h>(假设咱们为print_hello.c写了一个hello.h的头文件)。其实,只要编译命令中加入选项-lhello,hello.h头文件包含不包含都没有问题。为了验证这个问题,使用http://www.cnblogs.com/nufangrensheng/p/3518411.html中的程序清单11-1这个实例进行了测试:
在http://www.cnblogs.com/nufangrensheng/p/3518411.html程序清单11-1编译过程当中,曾遇到undefined reference to ‘pthread_create’这样的错误,缘由在于没有在编译命令中加入-lpthread选项。
若是咱们加入了-lpthread选项,此时尝试着把程序中的#include <pthread.h>去掉,从新编译你会发现一样也是能够的。
总结:若是使用的是标准库中的函数,则只需将头文件包含进来。若是使用的函数所在的库不是标准库(例如咱们本身编写的库或标准之外扩展的库),则在编译时必须加入-lxxx选项,而头文件则能够包含也能够不包含,包含进来显得规范而已。