1、linux库的概念
库是一种软件组件技术,库里面封装了数据和函数,提供给用户程序调用。库的使用可以使程序模块化,提高程序的编译速度,
实现代码重用,使程序易于升级。因此,对于软件开发人员,掌握这项技术是十分必要的。window系统本身提供并使用了大量的库,
包括静态链接库(.lib)和动态链接库(.dll)。类似的,linux系统也使用库。linux系统中,通常把库文件放在/usr/lib或/lib目录下。Linux库
文件由前缀lib、库名以及后缀3部分组成,其中动态库以.so作为后缀,而静态库通常以.a作为后缀。
在程序中使用静态库和动态库时,他们的载入顺序是不一样的。静态库的代码在编译时就拷贝到应用程序中,因此当多个应用程序
同时引用一个静态库函数时,内存中将会有调用函数的多个副本。这样的优点是节省编译时间。而动态库是在程序运行开始后调用
库函数时才被载入,被调函数在内存中只有一个副本,并且动态库可以在运行程序期间释放动态库所占用的内存,腾出空间供其他
程序使用。
2、静态库的创建和使用
创建静态库的步骤:
(1)在一个头文件中声明静态库所导出的函数
(2)在一个源文件中实现静态库所导出的函数
(3)编译源文件,生成可执行代码
(4)将可执行代码所在的目标文件加入到某个静态库中,并将静态库拷贝到系统默认的
存放库文件的目录下。
实例:
mylib.h中存放的是静态库提供给用户使用的函数的声明,mylib.c实现了mylib.h中声明的函数
头文件
#ifndef _mylib_h_
#define _mylib_h_
void welcome();
void outstring(const char *str);
#endif
源文件:
#include<stdio.h>
#include "mylib.h"
void welcome()
{
printf("welcome to lib\n");
}
void outstring(const char *str)
{
if ( !str )
{
printf("%s\n", str);
}
}
编译mylib.c生成目标文件
gcc -o mylib.o -c mylib.c
将目标文件加入到静态库中,静态库为libmylib.a
ar rcs libmylib.a mylib.o
将静态库拷贝到linux的库目录(/usr/lib或/lib)下:(也可不拷贝,在编译时指定静态库的路径)
cp libmylib.a /usr/lib/libmylib.a
以下为调用库函数的测试程序test.c
#include "lib.h"
int main()
{
weclome();
outstring("haha");
return 0;
}
编译test.c生成可执行文件
gcc -o main test.c -lmylib -L .
注意: -lmylib 中 -l 为选项。 mylib为库名。 mylib是 “libmylib.a”的中间部分,linux下约定所有库
都以前缀lib开始,静态库以 .a结尾。动态库以.so结尾。在编译程序时,无需带上前缀和后缀。
linux下,可以使用ar命令来创建和修改静态库。
ar rcs libmylib.a file1.o file2.o
该命令将目标代码file1.o 和 file2.o 加入到静态库libmylib.a中,加入时如果静态库libmylib.a不存在,则会
自动创建一个静态库。rcs是命令ar的选项。
以下是ar命令 的使用格式。
ar [emulation options] [-]{dmpqrstx}[abcDfilMNoPsSTuvV] 库名 库中的成员文件名
其中的 - 不是必需的, “dmpqrstx” 这些字母在每个命令中只能有一个且必须有一个
”abcDfilMNoPsSTuvV“中的字母在每个命令中可以有0到多个。
常用选项含义如下:
d:从库中删除成员文件
m:
p:
q:
r:
t:
x:
a:
b:
c:创建一个库
i:
l:
s:无论ar命令是否修改了库内容,都强制重新生成库符号表
u:
v:用来显示操作的附件信息
V:显示ar的版本信息
3、动态库的创建和使用
在linux环境下,可以很方便地创建和使用动态链接库。只要在编译函数库源程序时加上 -shared选项即可,这样所生成的可执行程序就为动态链接库。
从某种意义上讲,动态链接库也是一种可执行程序。
按一按规则,动态链接库以.so后缀。下面命令把mylib.c程序创建成了一个动态库:
gcc -fPIC -o lib.o -c lib.c
gcc -shared -o libmylib.so lib.o
也可以直接使用一条指令:
gcc -fPIC -shared -o libmylib.so lib.c
动态链接库创建后就可以使用了。
两种方法调用动态链接库中的函数:
(1)像使用静态库一样,通过gcc命令调用
gcc -o main test.c ./ libmylib.so
或
gcc -o main test.c -lmylib -L .
需要把 libmylib.so 放入到 系统指定的目录下(/etc/ld.so.conf 配置这个路径)
执行:
./main
(2)通过系统调用来使用动态链接库
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
打开指定名字的动态链接库,并返回一个句柄
char *dlerror(void);
动态链接库中的函数执行失败时,dlerror返回出错信息;若执行成功,则返回NULL
void *dlsym(void *handle, const char *symbol);
根据动态链接库的句柄与函数名,返回函数名对应的函数的地址
int dlclose(void *handle);
关闭动态链接库,handle是调用dlopen函数返回的句柄。
Link with -ldl.
以上的函数在库中,编译调用这些库函数的程序需要 链接库 -ldl
------------------------------------------------------
dlopen函数的参数flag可取的值有:
RTLD_LAZY:需要在dlopen返回前,对于动态库中存在的未定义的变量(如外部变量extern,也可以是函数)不执行解析,也就是
不解析这个变量的地址
RTLD_NOW:需要在dlopen返回前,解析出所有未定义符号,如果解析不出来,在dlopen会返回NULL,错误为:: undefined symbol: xxxx.......
RTLD_GLOBAL:使库中被解析出来的变量在随后的其他链接库中也可以使用,即全局有效。
实例:
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
int main()
{
void *handle;
char *error;
void (*welcome)();
if ((handle = dlopen("./libmylib.so", RTLD_LAZY)) == NULL)
{
printf("dlopen error:");
exit(1);
}
welcome = dlsym(handle, "weclome");
if ((error =dlerror()) != NULL)
{
perror("dlerror error:");
exit(1);
}
welcome();
dlclose(handle);
return 0;
}
编译:
gcc -o main test.c -ldl
./main
程序成功调用了动态链接库libmylib.so中的welcome函数。通过在程序中调用系统函数来使用动态链接库时,
动态链接库要指定所在的路径。
注意:这种调用方式,不要把动态库拷贝到系统指定的动态库目录下。