之前我们学习的是制作库文件,不管是静态库或者是共享库,根据具体的编译流程就可以制作,但是无论是什么库,使用的时候都需要链接。为了使库文件能够正确的链接,链接的时候需要能够定位库。对于静态库链接程序来说,所有的目标文件都集中在一起保存成一个独立的可执行文件,这个可执行文件完全可以移植到相互兼容的系统中并能正确运行,甚至目标文件都不存在也没有关系。但是共享库链接的时候必须存在,每次运行程序的时候需要使用。
无论在什么时候,只要链接程序就需要查找库,编译器会查找指定的目录列表。这些目录是否被包含进查询路径,依赖于使用哪种竞争模式 ld(ld是链接器),编译的时候如何配置 ld,以及命令行指定的目录。大多数的系统库放置的位置是目录/lib和/usr/lib中,这是系统配置好的,因此会自动查找这两个目录。通过使用一个或者多个-L选项,指定其他的查询目录。命令在执行的时候就会直接到我们指定的目录下寻找链接文件。
实例:/home/lib目录下存放着我们的库文件libtest.a(静态库),编译的时候命令如下:
执行命令结束,我们就可以得到最终的目标文件。
只要程序被链接想要使用共享库,就必须在运行的时候能够找到共享库的位置。因为是通过名字确定库,而不是通过目录定位库,因此可以在链接程序的时候使用一个库,而在运行的时候使用另一个库。如过我们改变库文件的版本号,而没有改变程序的版本,在运行的时候会出现问题,所以很多库会把版本号作为名字的一部分,例如:libm.so或libutil-2.2.4.so。
无论何时载入程序并打算运行的时候,共享库都应该位于以下的位置:
如果想要知道载入了那个库,以及确切的了解应用程序使用的那个库,可以使用ldd。命令格式如下:
option可以表示的选项如下:-d:进程数据重寻址-r:进程数据和函数重寻址-u:打印出未使用的直接依赖关系-v:打印出所有的信息实例:我们当前目录下存在一个可执行文件main。在命令行输入命令:
显示的信息如下:
上面显示的信息,文件链接的库文件,以及库文件的版本号和库的位置。
共享库中的函数可被加载并执行而不需要链接到程序里。加载动态链接库,首先要为共享库分配物理内存,然后在进程对应的页表项中建立虚拟页和物理页面之间的映射。动态加载函数需要使用四个基本函数分别是:dlopen()、dlsym()、dlerror()、dlclose(),使用这些函数需要包含头文件dlfcn.h。
分别介绍一下各个函数的作用和使用方式(我们也可以通过man手册查看)。
1. 打开动态链接库文件函数为 dlopen,函数原型如下:
dlopen用于打开指定名字(filename)的动态链接库(最好文件绝对路径),并返回操作句柄。
flag的选项可以是以下两种:
返回值:打开错误返回NULL,成功返回库引用。编译时要加入“-ldl”选项(指定dl库),实例:
2. 取函数执行地址的的函数为 dlsym,函数原型如下:
dlsym 根据动态链接库操作句柄 (handle) 与符号 (symbol) ,返回符号对应的函数的执行代码地址。
3. 关闭动态链接库函数为dlclose,函数原型如下:
dlclose 用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为 0 时,才会真正被系统卸载。返回值为 0 表示成功,非零表示失败。
4. 动态库错误函数为 dlerror,函数原型如下:
当动态链接库操作函数执行失败时,dlerror 可以返回出错信息,返回值为 NULL 时表示操作函数执行成功。
实例:只要两个函数向标准输出中显示字符,说明他们被调用了。
/*sayhello.c*/ #include <stdio.h> void sayhello() { printf("hello\n"); } /*saysomething.c*/ #include <stdio.h> void saysomething(char *str) { printf("%s\n",str); }
把上面这两个函数制作成静态库文件。使用命令:
调用函数如下:
/*main.c*/ #include <stdio.h> #include <dlfcn.h> #include <stdlib.h> int main(int argc,char *argv[]) { void *handler; char *error; void (*sayhello)(void); void (*saysomething)(char *); handler = dlopen("libsayfn.so",RTLD_LAZY); if(error = dlerror()) { printf("%s\n",error); exit(1); } sayhello = dlsym(handler,"sayhello"); if(error = dlerror()) { printf("%s\n",error); exit(1); } saysomething = dlsym(handler,"saysomething"); if(error = dlerror()) { printf("%s\n",error); exit(1); } sayhello(); saysomething("this is somethng"); dlclose(handler); return 0; }
最后编译main.c文件,使用下面的命令:
编译的时候需要注意,需要添加链接选项-ldl,还有就是在执行程序的时候需要把动态库文件放到指定的位置,否则就会出现动态库找不到的错误信息。运行程序就可以看到如下的信息:
这就说明我们这里例子没有问题。