1. linux中的库
库用于将相似函数打包在一个单元中。然后这些单元就可为其他开发人员所共享,并因此有了模块化编程这种说法。在linux下常见的库文件,就是*.so文件。Linux中绝大多数.so文件都存放在/lib、/usr/lib/(见Linux目录结构),对于64位和32位共存的系统,32位的动态库可能会放在/lib32.
2.动态库和静态库
Linux支持两种类型的库,每一种库都有各自的优缺点。静态库是在编译阶段随用户程序一块进行编译。动态库则不同,它是在加载应用程序时被加载的,而且它与应用程序是在运行时绑定的。
(1)静态链接——可执行程序包含了其所需的全部库函数;所有库函数都连接到程序中。 这类程序是完整的,其运行不需要外部库的支持。 静态链接程序的优点之一是其安装之前不需要做环境准备工作 。
(2)动态链接——可执行程序要小得多;这类程序运行时需要外部共享 函数库的支持,因此好像并不完整。除了程序体小之外,动态链接允许程序包指定必须的库,而不必将库装入程序包内。动态链接技术还允许多个运行中的程序共享一个库,这样就不会出现同一代码的多份拷贝共占内存的情况了。由于这些原因,当前多数程序采用动态链接技术。
在linux中的库有静态库和动态库两种,其中动态库又分为动态链接库和动态加载库两种。
3.动态链接库和动态加载库
GNU/Linux中共享库的使用主要有两种方式,一种方式和.a的静态库类似由编译器来控制,其实质和二进制程序一样都是由系统中的载入器(ld-linux.so)载入;另一种是写在代码中,由我们自己的代码来控制.
(1)动态链接库
您可以动态地将程序和共享库链接并让 Linux 在执行时加载库(如果它已经在内存中了,则无需再加载)。
(2)动态加载库
使用动态加载的过程,这样程序可以有选择地调用库中的函数。使用动态加载过程,程序可以先加载一个特定的库(已加载则不必),然后调用该库中的某一特定函数。这是构建支持插件的应用程序的一个普遍的方法。这种方法需要使用linux提供的动态加载DL API来控制加载库以及使用库中的函数。DL提供的API如下表所示:
表 1. Dl API
函数 | 描述 |
---|---|
dlopen | 使对象文件可被程序访问 |
dlsym | 获取执行了 dlopen 函数的对象文件中的符号的地址 |
dlerror | 返回上一次出现错误的字符串错误 |
dlclose | 关闭目标文件 |
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#define MAX_STRING 80
void invoke_method( char *lib, char *method, float argument )
{
void *dl_handle;
float (*func)(float);
char *error;
/* Open the shared object */
dl_handle = dlopen( lib, RTLD_LAZY );
if (!dl_handle) {
printf( "!!! %s\n", dlerror() );
return;
}
/* Resolve the symbol (method) from the object */
func = dlsym( dl_handle, method );
error = dlerror();
if (error != NULL) {
printf( "!!! %s\n", error );
return;
}
/* Call the resolved method and print the result */
printf(" %f\n", (*func)(argument) );
/* Close the object */
dlclose( dl_handle );
return;
}
int main( int argc, char *argv[] )
{
char line[MAX_STRING+1];
char lib[MAX_STRING+1];
char method[MAX_STRING+1];
float argument;
while (1) {
printf("> ");
line[0]=0;
fgets( line, MAX_STRING, stdin);
if (!strncmp(line, "bye", 3)) break;
sscanf( line, "%s %s %f", lib, method, &argument);
invoke_method( lib, method, argument );
}
}
4.静态链接库的定义和使用方式
(1)静态链接库的定义
通过编译的预处理、编译阶段生成的目标文件就可以作为静态链接库。
(2)静态链接库的使用
有两种方式,一种是g++ -o main main.cpp ./libullib.a,直接将libullib.a库链接到二进制程序main中;另一种是g++ -o main main.cpp -L./ -lullib,这种和动态链接库的使用方式一样,但是生成静态链接库和动态链接库的方式是不一样的。
5.共享库的定义和使用方式,
(1)共享库的定义
生成共享库和生成静态库类似,只是加上了 -shared 和 -fPIC,将输出命名改为.so,这样生成的执行程序即为共享库。
g++ -shared -fPIC -o libx.so libx.cpp
(2)共享库的使用
两种方式,一种是g++ -o main main.cpp -L./ -lx,通过gcc/g++的-l和-L引入。一种是程序中显示控制。
6.linux中库的查找顺序
(1)Linux gcc编译以及链接时对依赖的库的查找顺序
GCC编译、链接生成可执行文件时,对依赖库的搜索路径顺序如下(注意不会递归性地在其子目录下搜索):
1.gcc编译、链接命令中的-L选项
2.gcc的环境变量的LIBRARY_PATH(多个路径用冒号分割)
3.gcc默认动态库目录:/lib:/usr/lib:usr/lib64:/usr/local/lib
(2)执行二进制文件时的对依赖的共享库的查找顺序
链接生成二进制可执行文件后,在运行程序加载动态库文件时,搜索的路径顺序如下:
1.编译目标代码时指定的动态库搜索路径:用选项-Wl,rpath和include指定的动态库的搜索路径,比如gcc -Wl,-rpath,include -L. -ldltest hello.c,在执行文件时会搜索路径`./include`
2.环境变量LD_LIBRARY_PATH(多个路径用冒号分割)
3.由ldconfig工具构建的/etc/ld.so.cache缓存文件中查找
4.gcc默认动态库目录:/lib:/usr/lib:usr/lib64:/usr/local/lib等
7.共享库的加载
对于依赖共享库的可执行文件在真正执行前,实际上它的很多外部符号还处于无效状态,还未与实际的so文件联系起来,因此还有一个动态链接过程。操作系统会首先加载动态链接器ld.so(/lib/ld-linux.so.2),加载完这个so后,系统就会把控制权交给它,然后它会进行一些初始化操作,根据当前环境参数对可执行文件进行动态链接工作。动态链接器会寻找所需要的.so文件并进行装载,然后进行符号查找及重定位。如果找不到所需要的符号定义就会产生“undefined symbol”错误。
8.库文件生成,修改工具ar
ar命令可以用来创建、修改库,也可以从库中提出单个模块。库是一单独的文件,里面包含了按照特定的结构组织起来的其它的一些文件(称做此库文件的member)。原始文件的内容、模式、时间戳、属主、组等属性都保留在库文件中。
ar工具的详细使用可以参见http://blog.csdn.net/xuhongning/article/details/6365200
学习资料来源于:
http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/
http://blog.csdn.net/xuhongning/article/details/6365200
https://typecodes.com/cseries/gcclderrlibrarypath.html