linux库文件编写入门

来源:互联网 发布:软件著作权申请表填写 编辑:程序博客网 时间:2024/06/02 03:02

一、静态库的编写和使用

1、概述

静态库文件的扩展名一般为.a,其编写步骤很简单。

⑴编写函数代码

⑵编译生成各目标文件

⑶用ar文件对目标文件归档,生成静态库文件.注意归档文件名必须以lib打头。

  使用要点:

  ⑴在gcc-i参数后加上静态库头文件的路径

  ⑵在gcc-l参数后加上库文件所在目录

  ⑶在gcc-l参数后加上库文件名,但是要去掉lib.a扩展名

    比如库文件名是libtest.a那么参数就是 -ltest

 

2、编写最简单的静态库文件

编写如下两个文件,注意放在同一目录中

  myalib.h   //静态库头文件

  myalib.c   //静态库实现文件

  

  //myalib.h  文件的内容

void test();

 

//myalib.c  文件的内容

#inlcude

void test()

{

             printf("test\n");

}

 

3、制作库文件

⑴生成目标文件

 gcc-c myalib.c

 执行完后会生成一个myalib.o文件

⑵用ar命令归档,格式为ar-rc 

再次提醒,归档文件名一定要以lib打头, .a结尾

 ar-rc libtest.a myalib.o

 执行完后会生成一个libtest.a文件

 

4、使用库文件

⑴编写一个测试程序main.c,内容为

//main.c测试静态库调用的程序

#include"myalib.h"   //要把函数的头文件包含进来,否则编译时会报错

 intmain(int argc,char* argv[])

{

        test();

        return 0; 

}

⑵编译目标文件,注意要把静态库头文件的路径加到-i参数里面

  gcc-i /root/exercise -o main.o -c main.c

  现在生成了一个main.o文件

⑶生成可执行文件,注意要把静态库文件的路径加到-l参数里面,

把库文件名(去掉打头的lib和结尾的.a)加到-l参数后面

 gcc-o main -l/root/exercise   main.o -ltest

 此时就会生成一个名为main的可执行文件,另外,注意- l参数应该加到输入文件名的后面,否则会报错

 比如gcc -o main -l/root/exercise -ltest main.o就会提示

 main.o(.text+0x11): in function `main':

 :undefined reference to `test'

 collect2: ld returned 1 exit status

 (原因我还不清楚:-)

⑷执行可执行文件查看效果

 执行./main 输出

 test

 说明执行成功。

 

二、动态库的编写

1、概述

  动态库一般以.so结尾,就是sharedobject的意思.

  其基本生成步骤为

  ⑴编写函数代码

  ⑵编译生成动态库文件,要加上 -shared -fpic 选项库文件名以lib开头,以.so结尾。

   使用方式分为两种:隐式调用和显示调用 

   隐式调用类似于静态库的使用,但需修改动态链接库的配置文件/etc/ld.so.conf;

   显示调用则是在主程序里使用dlopendlsymdlerrordlclose等系统函数。

2、编写最简单的动态库文件

   为了便于对照,我们仍然采用静态库中的文件做例子.

   编写如下两个文件,注意放在同一目录中

   myalib.h  //静态库头文件

   myalib.c  //静态库实现文件

  

//myalib.h  文件的内容

voidtest();

 

//myalib.c  文件的内容

#inlcude

voidtest()

{

printf("test\n");

}

 

3、编译生成动态库 ,库文件名以lib开头,.so结尾。

gcc-fpic -shared -o libtest.so  myalib.c

此时就生成一个libtest.so文件

 

五、动态库的隐式调用

 隐式调用的含义是代码里不出现库文件名,就是说这个代码和调用静态库的代码是类似的。

1、编写测试文件

//main.c测试动态库隐式调用的程序

#include"myalib.h"   //要把函数的头文件包含进来,否则编译时会报错

 intmain(int argc,char* argv[])

{

        test();

        return 0; 

}

2编译测试程序,与静态库类似,要把头文件的路径加到-i参数里面

 gcc-i /root/exercise -o main.o -c main.c

 现在生成了一个main.o文件

3、连接生成测试程序 

 gcc-o main -l/root/exercise   main.o -ltest

 现在生成了一个main文件

4、执行测试程序

 ./main

 出现提示

  ./main: error while loading shared libraries: libtest.so: cannot openshared object file: no such file or directory

   这个原因就是程序运行时并不知道动态库所在的路径,因此自然找不到。

   解决这个问题的办法有三种。见下节

六、使动态库被系统共享的三种办法

http://www.ccw.com.cn/htm/center/prog/02_3_13_3_2.asp

(1)拷贝动态链接库到系统共享目录下,或在系统共享目录下为该动态链接库

  建立连接(硬连接或符号连接均可,常用符号连接).这里说的系统共享目录,指的是linux动态链接库存放的目录,包括/lib,/usr/lib以及/etc/ld.so.conf文件内所列的一系列目录.

 

  实例:执行

# cplibtest.so /lib

#ldconfig

  :

  # ln -s `pwd`/libtest.so /lib

#ldconfig

注意pwd前后有两个反引号`,其目的是取得pwd命令的输出,即当前目录.此时再执行main,即可成功.

 

(2)将动态链接库所在目录名追加到动态链接库配置文件/etc/ld.so.conf.

# pwd >> /etc/ld.so.conf

# ldconfig

 

此时再执行main,即可成功.

 

(3)利用动态链接库管理命令ldconfig,强制其搜索指定目录,并更新缓存文件,便于动态装入.

# ldconfig `pwd`

  此时再执行main,即可成功.

 

  要注意,第三种方法虽然有效,但效果是暂时的,供程序测试还可以,一旦再度运行ldconfig,则缓存文件内容可能改变,所需的动态链接库可能不被系统共享了.而且无论哪种办法,其实质都是用ldconfig命令把动态库文件。所在路径加入到系统库列表中,(前两种永久,第三种临时)

  

七、动态库的显式调用

 显式调用的含义是代码出现库文件名,用户需要自己去打开和管理库文件。其要点为:

 ⑴把dlfcn.h系统头文件包含进来

 ⑵用dlopen函数打开库文件,并指定打开方式

dllope的的第一个参数为共享库的名称,将会在下面位置查找指定的共享库。

 ①环境变量ld_library_path列出的用分号间隔的所有目录。

 ②文件/etc/ld.so.cache中找到的库的列表,由ldconfig命令刷新。

 ③目录usr/lib

 ④目录/lib

 ⑤当前目录。

第二个参数为打开共享库的方式。有两个取值

 rtld_now:将共享库中的所有函数加载到内存

 rtld_lazy:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数

 ⑶用dlerror()函数测试是否打开成功,并进行错误处理;

 ⑷用dlsym获得函数地址,存放在一个函数指针中

 ⑸用获得的函数指针进行函数调用。

 ⑹程序结束时用dlclose关闭打开的动态库,防止资源泄露。

 ⑺用ldconfig工具把动态库的路径加到系统库列表中

 

1、编写测试文件

//main.c 测试动态库显式调用的程序

#include      //用于动态库管理的系统头文件  

#include "myalib.h"    //要把函数的头文件包含进来,否则编译时会报错

 intmain(int argc,char* argv[])

{

        //声明对应的函数的函数指针

        void (*ptest)();   

     

     //加载动态库

        void *pdlhandle =dlopen("libtest.so", rtld_lazy); 

 

        //错误处理

     if(pdlhandle== null )

{

       printf("failed load library\n");

       return -1;

     }

     char* pszerr = dlerror();

     if(pszerr != null)

     {

       printf("%s\n", pszerr);

       return -1;

     }

   

   //获取函数的地址

   ptest = dlsym(pdlhandle, "test");

   pszerr = dlerror();

   if(pszerr != null)

    {

       printf("%s\n", pszerr);

       dlclose(pdlhandle);

       return -1;

    }

   

   //实现函数调用

   (*ptest)();

 

 //程序结束时关闭动态库

 dlclose(pdlhandle);

 return 0; 

 

2、编译测试文件使用-ldl选项指明生成的对象模块需要使用共享库

gcc -o main -ldl main.c

执行完后就生成了一个main文件

3、执行测试程序

执行 ./main

输出

  test

   说明成功。

 

六、使用动态库时应注意的其他问题

1、无论是动态库的显式调用还是隐式调用,都需要用

ldconfig工具将动态库的路径加到系统库列表中,否则运行时会出错。  

2、可以用ldd命令检查程序都使用到哪些共享库

ldd命令行用法如下:

ldd [--version] [-v|--verbose][-d|--data-relocs] [-r|--function-relocs] [--help] file...

 各选项说明如下:

 (1)--version : 此选项用于打印出ldd的版本号.

 (2)-v --verbose :此选项指示ldd输出关于所依赖的动态链接库的尽可能详细的信息.

 (3)-d --data-relocs :此选项执行重定位,并且显示不存在的函数.

 (4)-r --function-relocs :此选项执行数据对象与函数的重定位,同时报告不存在的对象.

 (5)--help : 此选项用于打印出ldd的帮助信息.

 我们一般用-v选项.

 

现在看几个实例

 ⑴用静态库连接时的结果 

#ldd main

libc.so.6 => /lib/tls/libc.so.6(0xb74ad000)

/lib/ld-linux.so.2 => /lib/ld-linux.so.2(0xb75eb000)

可见使用静态库时,由于库已经被编译成程序的一部分,因此ldd的输出中就只有用到的

系统库。

 

⑵用动态库隐式连接时的结果

 libtest.so => /root/exercise/libtest.so(0xb75e2000)

   libc.so.6 => /lib/tls/libc.so.6 (0xb74ab000)

   /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)

    可见隐式使用动态库时,所有用到的动态库(包括系统和用户的)都会被显示出来。

 

⑶动态库显式连接时的结果

ldd main

libdl.so.2 => /lib/libdl.so.2(0xb75e1000)

  libc.so.6 => /lib/tls/libc.so.6 (0xb74aa000)

  /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)

可见显式使用动态库时,程序中不再保存运行时打开动态库的信息,只保留用到的系统库的信息.  

这个与使用静态库时的输出是类似的.

 

 特别函数_init_fini
 函数库里面有两个特别的函数,_init_fini,这个我们在前面已说过了。主要是分别用来初始化函数库和关闭的时候做一些必要的处理,我们能把自己认为需要的代码放到这两个函数里面,他们分别在函数库被加载和释放的时候被执行。具体说,如果一个函数库里面有一个名字为“_init”的函数输出,那么在第一次通过dlopen()函数打开这个函数库,或只是简单的作为共享函数库被打开的时候,_init函数被自动调用执行。和之相对应的就是_fini函数,当一个程式调用dlclose()去释放对这个函数库的引用的时候,如果该函数库的被引用计数器为0了,或这个函数库是作为一般的共享函数库被使用而使用他的程式正常退出的时候,_fini就会被调用执行。C语言定义他们的原型如下:void _init(void); void _fini(void);当用gcc编译源程式为“.o”文件的时候,需要加一个“-nostartfiles”选项。这个选项使得C编译器不链接系统的启动函数库里面的启动函数。否则,就会得到一个“multiple-definition”的错误。

动态库的搜索路径搜索的先后顺序是:

1.编译目标代码时指定的动态库搜索路径;

2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

3.配置文件/etc/ld.so.conf中指定的动态库搜索路径;

4.默认的动态库搜索路径/lib

5.默认的动态库搜索路径/usr/lib

 

原创粉丝点击