Lib库使用学习笔记.docx

上传人:b****1 文档编号:2027564 上传时间:2023-05-02 格式:DOCX 页数:19 大小:25.23KB
下载 相关 举报
Lib库使用学习笔记.docx_第1页
第1页 / 共19页
Lib库使用学习笔记.docx_第2页
第2页 / 共19页
Lib库使用学习笔记.docx_第3页
第3页 / 共19页
Lib库使用学习笔记.docx_第4页
第4页 / 共19页
Lib库使用学习笔记.docx_第5页
第5页 / 共19页
Lib库使用学习笔记.docx_第6页
第6页 / 共19页
Lib库使用学习笔记.docx_第7页
第7页 / 共19页
Lib库使用学习笔记.docx_第8页
第8页 / 共19页
Lib库使用学习笔记.docx_第9页
第9页 / 共19页
Lib库使用学习笔记.docx_第10页
第10页 / 共19页
Lib库使用学习笔记.docx_第11页
第11页 / 共19页
Lib库使用学习笔记.docx_第12页
第12页 / 共19页
Lib库使用学习笔记.docx_第13页
第13页 / 共19页
Lib库使用学习笔记.docx_第14页
第14页 / 共19页
Lib库使用学习笔记.docx_第15页
第15页 / 共19页
Lib库使用学习笔记.docx_第16页
第16页 / 共19页
Lib库使用学习笔记.docx_第17页
第17页 / 共19页
Lib库使用学习笔记.docx_第18页
第18页 / 共19页
Lib库使用学习笔记.docx_第19页
第19页 / 共19页
亲,该文档总共19页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Lib库使用学习笔记.docx

《Lib库使用学习笔记.docx》由会员分享,可在线阅读,更多相关《Lib库使用学习笔记.docx(19页珍藏版)》请在冰点文库上搜索。

Lib库使用学习笔记.docx

Lib库使用学习笔记

Lib库使用学习笔记

一、Chapter 1. 为什么使用库文件

我们在实际编程工作中肯定会遇到这种情况:

有几个项目里有一些函数模块的功能相同,实现代码也相同,也是我们所说的重复代码。

比如,很多项目里都有一个用户验证的功能。

代码段如下:

//UserLogin.h文件,提供函数声明

intIsValidUser(char*username,intnamelen);

//UserLogin.c文件,实现对用户信息的验证

intIsValidUser(char*username,intnamelen)

{

intIsValid=0;

/*下面是具体的处理代码,略去*/

returnIsValid;

}

如果每个项目都保存着这两个UserLogin.h和UserLogin.c文件,会有以下几个弊端:

1.每个项目里都有重复的模块,造成代码重复。

2.代码的重用性不好,一旦IsValidUser的代码发生了变化,为了保持设计的一致性,我们还要手工修改其他项目里的UserLogin.c文件,既费时又费力,还容易出错。

库文件就是对公共代码的一种组织形式。

为了解决上面两个弊端,就提出了用库文件存放公共代码的解决方案,其要点就是把公共的(也就是可以被多次复用的)目标代码从项目中分离出来,统一存放到库文件中,项目要用到这些代码的时候,在编译或者运行的时候从库文件中取得目标代码即可。

库文件又分两种:

静态库和动态库。

二、Chapter 2. 静态库和动态库

简单的说,如果程序是在编译时加载库文件的,就是使用了静态库,静态库的文件名格式是"lib*.a"。

如果是在运行时加载目标代码,就成为动态库,动态库的文件名格式是"lib*.so.*"。

换句话说,如果是使用静态库,则静态库代码在编译时就拷贝到了程序的代码段,程序的体积会膨胀。

如果使用动态库,则程序中只保留库文件的名字和函数名,在运行时去查找库文件和函数体,程序的体积基本变化不大。

静态库的原则是“以空间换时间”,增加程序体积,减少运行时间;动态库则是“以时间换空间”,增加了运行时间,但减少了程序本身的体积。

在附录的PIC部分,也会做一些说明。

从文件的格式角度讲,静态库的本质是一种档案文件(.o文件的集合),其中包含了一个内容索引(也可以不包含,但没有索引的静态库不能用来链接,在附录的ar 和各个模块既.o文件;而动态库是ELF格式的文件,可以被用来加载和执行,而静态库不可以。

还有一种库文件,共享库。

看到网上有些资料说,动态库就是共享库的一种变种,由于没有使用到,没有详细研究。

有时候,会在目录中看到以".la"或".lo"结尾的文件,这些是GNU的工具libtool生成和使用的文件,用来说明实际库文件的使用信息和以来关系,详细的内容会在以后automakeautoconfandlibtool的文档中介绍。

三、Chapter 3. 静态库的生成和使用

3.1. 制作最简单的静态库文件

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

mylib.h//静态库头文件

voidtest();

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

#include

voidtest()

{

printf("helloworld./n");

}

使用下边的命令生成静态库:

gcc-cmylib.c

arrclibmy.amylib.o

这样就生成了静态库libmy.a,注意一定要以lib*.a这样的格式命名,否则链接器ld不能识别。

使用命令"filelibmy.a"看看它的格式,是一个档案文件。

我们可以使用 nm查看它的内部构成:

[root@Bensonlibtest]#nmlibmy.a

libmy.o:

Uprintf

00000000Ttest

这表示静态库有模块libmy.o,在使用的时候,gcc会根据需要将函数名得到模块,然后从静态库中提取出对应的".o"文件的内容,然后用来链接,就是使用单独的".o"文件一样。

3.2. 使用库文件

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

#include"mylib.h"

intmain(void)

{

test();

return0;

}

由于需要调用libmy.a中的test函数,所以在编译时,需通过"-L-l"参数指定链接这个库:

gcc-I./-omainmain.c-L./-lmy

通过-I和-L参数制定了gcc的头文件和库文件搜索路径为当前目录,也可以根据需要指定为其他目录。

生成执行文件main后,执行命令"filemain",可以看到:

[root@Bensonlibtest]#filemain

main:

ELF32-bitLSBexecutable,Intel80386,version1(SYSV),forGNU/Linux2.2.5,

staticllylinked(usessharedlibs),notstripped

运行,看到结果,成功。

四、Chapter 4. 动态库的生成和使用

 

4.1. 制作最简单的动态库文件

 

我们同样使用上边的mylib.h和mylib.c文件,但使用不同的命令生成库文件:

gcc-fpic-sharedlibmy.solibmy.c

这样就生成了动态库文件libmy.so,-fpic这个选项指定是否使用 PIC,这个选项的使用需要系统平台的支持,一般建议添加,如果不支持,gcc会报错。

生成libmy.so后,使用"filelibmy.so"命令,可以看到是一个ELF格式的文件,这说明共享库的使用需要通过符号解析和重定位加载入内存才能使用。

4.2. 动态库的隐式调用

动态库有两种使用方法,隐式调用和显示调用。

隐式调用的方法跟静态库的使用方法一样,都是通过gcc的"-I-L"参数指定库文件的路径,如果同一个库文件,在系统中同时存在静态库和动态库,默认情况下gcc主动链接动态库。

但也可以通过gcc的"--static"选项,强制指定使用静态库。

gcc-I./-omainmain.c-L./-lmy#使用动态库生成main

gccmain.c./libmy.so-omain#使用动态库生成main,不同的地方在于指定了libmy.so

的加载路径,这种用法很少用。

详细的内容附录ld.so

gcc--static-I./-omainmain.c-L./-lmy#使用静态库生成main

输入"filemain"命令,可以看到:

[root@Bensonlibtest]#filemain

main:

ELF32-bitLSBexecutable,Intel80386,version1(SYSV),forGNU/Linux2.2.5,

dynamicallylinked(usessharedlibs),notstripped

运行程序,报错:

[root@Bensonlibtest]#./main

./main:

errorwhileloadingsharedlibraries:

libmy.so:

cannotopensharedobjectfile:

Nosuchfileordirectory

没有找到libmy.so这个动态库,我这个库文件就在当前目录下?

4.3. 使动态库被系统共享的方法

前边已经说过动态库需要加载到内存中,才能使用。

注意,链接和加载是两码事,链接发生在生成可执行程序的阶段,加载发生在运行阶段。

gcc的"-I-L"参数只是保证在链接阶段指定文件路径,和加载无关,当然也有办法可以使链接阶段的路径信息在加载阶段起作用,这些涉及到gcc的详细用法,和可执行文件的格式,附录中会简单介绍一些。

运行时的加载的工作不是由main完成的,而是由/lib/ld.so(不同平台会有些出入)完成。

他有自己的查找动态连接库的规则,所以在不更新ld.so的查找路径的情况下,会出现上边的错误。

根据ld.so的man手册,ld.so共有六种方式查找需要的动态库,这里只介绍常用的三种方式,详细的内容见附录ld.so

1.按照环境变量LD_LIBRARY_PATH的内容查找(setuid类的程序排除)

2.在库高速缓存文件ld.so.conf中给出的路径查找

3.在/lib,/usr/lib目录下查找

相对应的,可以分别通过设置环境变量LD_LIBRARY_PATH,通过在ld.so.conf文件中添加路径和将动态库文件复制到/lib、/usr/lib目录下,使动态库可以在加载时被找到。

[root@Bensonlibtest]#exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:

/`pwd`

[root@Bensonlibtest]#cat`pwd`>>/etc/ld.so.conf;ldconfig

[root@Bensonlibtest]#cplibmy.so/lib-f

以上三种方式都可以工作。

关于ldconfig的使用详解,见 附录ldconfig

此时再执行main,可以正常运行了。

通过ldd命令,可以看到main程序依赖于libmy.so。

[root@Bensonlibtest]#lddmain

libtest.so=>notfound

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

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

4.4. 动态库的显式调用

动态库的显示调用,也需要库文件被加载到内存中。

但是使用方法和静态库完全不同。

需要系统调用——dlopendlsymdlclosedlerror的支持。

可以使用同一个libmy.so文件,但是需要新的测试文件:

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

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

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

intmain(void)

{

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

void(*pTest)();

/*加载动态库*/

void*pdlHandle=dlopen("libtest.so",RTLD_LAZY);

/*错误处理*/

if(pdlHandle==NULL)

{

printf("Failedloadlibrary/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);

return0;

}

编译时需要加入-ldl选项:

[root@Bensonlibtest]#gcc-ldl-omainmain.c

运行程序,成功。

这种方式,不需要在链接阶段指定动态库的位置。

在运行阶段,调用要使用的函数

通过ldd命令,可以看到main程序不再依赖于libmy.so,转为依赖libdl.so

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

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

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

Chapter 5. 链接时同时使用动态库和静态库

当一个执行程序链接时需要多个库文件时,通常情况下,我们要么统一采用静态链接,要么统一采用动态链接。

实际上,我们可以通过gcc的选项分别指定每种库的链接方式:

-Wl-Bstatic;-Wl-Bdynamic。

gcc-omainmain.c-Wl-Bstatic-lc-Wl-Bdynamic-test

上边这个例子中制定了静态链接libc和动态连接libtest。

而实际上gcc选项-Wl表示将后边的参数传给链接器,-Bstatic和-Bdynamic是ld的选项。

Appendix A. 附录

A.1. ldconfig

ldconfig用来维护ld.so加载的动态库的路径信息和依赖信息,路径信息存放在/etc/ld.so.conf中,/etc/ld.so.cache中存放了/lib/usr/lib和/etc/ld.conf包含的路径内的所有库信息。

可以通过ldd命令查看动态库的依赖信息。

ldconfig有两种常用的使用方法:

1.将需要查找的目录加入/etc/ld.so.conf,然后运行ldconfig更新,更新结果永久有效

2.ldconfig+路径,这种方式能直接更新指定的目录,但是结果在下次执行ldconfig时,将会失效。

ldconfig的选项比较有用的是-v和-n,其他的可参考man手册:

∙-v或--verbose详细的显示扫描的目录及搜索到的动态连接库,还有它所创建的链接的名字

∙-n使扫描时,不扫描默认目录/lib、/usr/lib和/etc/ld.so.conf包含的路径

A.2. ldd

ldd命令很简单,用来显示文件所依赖的动态库文件,同样-v选项显示详细信息。

A.3. ar

ar命令用来创建归档文件,修改归档文件,和提取归档文件中的模块。

这里归档文件就是静态库,模块就是里边的各个".o"文件。

在静态库中每个".o"文件的内容,权限,时间戳,文件所有者,文件所属组都将被保留,并且可在提取时回复。

ar通过在静态库中创建模块列表来维护内容结构。

可以通过"nm-s"命令查看这个列表内容。

ar命令的选项很多,可通过查看man手册获取全部内容,这里我举几个例子来说明它的主要功能:

1.创建静态库文件:

2.ar-rclibtest.alibmy.olibtest.o

选项分为"r"和"c"。

-r表示将后边的".o"模块加入库文件中;-c表示当库文件不存在时创建。

同样,向存在的库文件添加新的模块时,只需要-r选项即可。

3.查看静态库中的模块:

4.[root@Bensonlibtest]#ar-tlibtest.a

5.libmy.o

6.libtest.o

7.删除静态库中的模块:

8.ar-dlibtest.alibmy.o

此时在查看库的模块,libmy.o就不存在了。

9.提取静态库中的模块:

10.[root@Bensonlibtest]#ls

11.libtest.a

12.[root@Bensonlibtest]#ar-xlibtest.alibmy.o;ls

13.libtest.alibmy.o

从libtest.a中提取出了libmy.o文件

A.4. nm

nm命令用来查看目标文件中的符号信息,nm命令对每个符号显示如下内容:

∙每个符号的值。

∙当指定-S选项时,显示函数符号对应的函数体的大小。

∙每个符号的类型,如,U表示该符号没有在库中定义;T表示该符号在库中定义。

还有其他的内容涉及到目标文件的格式,这里就不详细介绍。

∙每个符号的名字。

nm的选项中,"-s"选项用来显示静态库文件的列表信息。

其他选项可查看man手册。

A.5. ranlib

为静态库生成索引信息,并将索引信息保存在静态库文件中,它是ar-s命令的变形。

带有索引的静态库能够加快连接速度,并允许库中的函数相互调用,而不需要考虑函数位置的先后。

A.6. PIC

PIC(PositionIndependentCode),位置无关编码。

是一种库文件的编码组织方式,其特点是方便系统装载。

是否支持这种格式的动态库,与硬件系统平台有关。

简单的说,这种格式的库文件包含两个符号GOT(_GLOBAL_OFFSET_TABLE_)和PLT(ProcedureLinkageTable)。

调用函数实际调用的是PLT里的地址,访问全局变量通过GOT里边的地址这些地址在未运行时不确定,在加载运行后,才被解析出来,所以可以加载到进程地址空间的任何地方。

摘自网络的一篇文章,含有更多信息:

PICcoderadicallydiffersfromconventionalcodeinthewayitcallsfunctionsandoperatesondatavariables.

Itwillaccessthesefunctionsanddatathroughanindirectiontable,the"GlobalOffsetTable"(GOT),bysoftwareconventionaccessibleusingthereservedname"_GLOBAL_OFFSET_TABLE_".

Theexactmechanismusedforthisishardwarearchitecturedependent,butusuallyaspecialmachineregisterisreservedforsettingupthelocationoftheGOTwhenenteringafunction.

Therationalebehindthisindirectaddressingistogeneratecodethatcanbeindependentlyaccessedoftheactualloadaddress.

InatruePIClibrarywithoutrelocationsinthetextsegment,onlythesymbolsexportedinthe"GlobalOffsetTable"needupdatingatrun-timedependingonthecurrentloadaddressofthevarioussharedlibrariesintheaddressspaceoftherunningprocess.

Likewise,procedurecallstogloballydefinedfunctionsareredirectedthroughthe"ProcedureLinkageTable"(PLT)residinginthedatasegmentofthecoreimage.Again,thisisdonetoavoidrun-timemodificationstothetextsegment.

Thelinker-editorallocatestheGlobalOffsetTableandProcedureLinkageTablewhencombiningPICobjectfilesintoanimagesuitableformappingintotheprocessaddressspace.Italsocollectsallsymbolsthatmaybeneededbytherun-timelink-editorandstoresthesealongwiththeimage'stextanddatabits.Anotherreservedsymbol,_DYNAMICisusedtoindicatethepresenceoftherun-timelinkerstructures.Whenever_DYNAMICisrelocatedto0,thereisnoneedtoinvoketherun-timelink-editor.Ifthissymbolisnon-zero,itpointsatadatastructurefromwhichthelocationofthenecessaryrelocation-andsymbolinformationcanbederived.Thisismostnotablyusedbythestart-upmodule,crt0,crt1SandmorerecentlyScrt1.The_DYNAMICstructureisconventionallylocatedatthestartofthedatasegmentoftheimagetowhichitpertains.

Onmostarchitectures,whenyoucompilesourcecodetoobjectcode,youneedtospecifywhethertheobjectcodeshouldbepositionindependentornot.Thereareoccasionalarchitectureswhichdon'tmakethedistinction,usuallybecauseallobjectcodeispositionindependentbyvirtueoftheApplicationBinaryInterface(ABI),orlessoftenbecausetheloadaddressoftheobjectisfixedatcompiletime,whichimpliesthatsharedlibrariesarenotsupportedbysuchaplatform).Ifanobjectiscompiledaspositionindependentcode(PI

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2