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函数。通过在程序中调用系统函数来使用动态链接库时,

动态链接库要指定所在的路径。

注意:这种调用方式,不要把动态库拷贝到系统指定的动态库目录下。