您的位置:首页 > 运维架构 > Linux

静态库&&动态库

2016-03-07 11:40 260 查看
编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。

链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。

在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File. (http://blog.sina.com.cn/s/blog_56aec1cb0100sfz4.html)             test.h中有函数sum(),mul()的声明,sum.c,mul.c必须include
test.h.否则能通过编译,但main.c无法与之链接。Main.c也要include test.h.

静态库的提出:

一个程序他所要用到的外部的函数和变量很多,于是他所要用到的目标模块也很多,1. 如果用gcc main.c sum.o mul.o这样显式地写出,容易出错而且耗时,2. 如果把所有的可重定位目标模块整合成一个可重定位目标模块(如libc.o),这样比较便利,但链接时main.o便需要与整个libc.o合并,生成可执行文件,这很浪费磁盘空间,并且对任何libc.o中函数的改变,都要求开发人员重新编译整个源文件,这使得维护变得复杂。

静态库,所有的相关源文件被编译成独立的目标模块(gcc –c mul.c sum.c),然后封装成一个单独的静态库文件(ar rcs libtest.a mul.o sum.o,r选项是replace的意思,当插入的模块已在库中存在,则替换同名模块,这样保证了只需编译一个源文件便可更新静态库。C是create的意思,创建静态库,s是往库中添加函数模块的意思,详见链接http://blog.csdn.net/freeman125384977/article/details/6697278),在链接时,链接器只拷贝被程序引用的目标模块。静态库以一种存档的格式保存在磁盘。

g++ -c sum.c mul.c

ar rc libtest.a sum.o mul.o

g++ -c main.c

g++ -static main.o –L. –ltest  (-l指定函数库名)   此时得到可执行文件a.out,并且就算删除了库libtest.a,它依然能正常执行

如果库要更新,比如要更新sum.c模块,只需

g++ -c sum.c

ar -r libtest.a sum.o

g++ -static main.c -L. –ltest 这样就会产生新的执行文件了。可以看出对库的更新无需编译全部的源文件。维护比较简单。但也可以看出她的一个缺点,如果函数库改变了,那么main.c要重新链接。并且在链接时她会把被程序引用的目标模块拷贝进来,这是很浪费磁盘空间的。详见(http://wenku.baidu.com/view/f09a388acc22bcd126ff0c70.html)

于是提出了动态链接共享库(DLL):

他在链接的时候并没有被拷贝进可执行文件中,可执行文件执行到相关函数时才调用该函数库的相应函数(共享库是一个目标模块,在运行时可以加载到任意的存储器地址,并和在存储器上的程序链接起来)因此使用动态库产生的可执行文件较小。

g++ -shared -fPIC -o libtest.so sum.c mul.c

g++ -o p test.c ../libtest.so (最后面的指定了库文件所在的路径, 并且库的路径指定死了的,无法修改,只要libtest.so移到别的目录下了,p就无法再执行。)

库中模块更改时只需再重新编译一下库g++ -shared -fPIC -o libtest.so sum.c mul.ctest.c无需再编译,p的运行结果也会跟着模块一起变化了。

./p 运行正常

g++ -o p test.c -L. –ltest  (静态执行链接)

./p(程序加载,动态完成链接过程,需要从是系统找到test库,因此要指定路径,都则报错)(ldd p 查看链接的库)

由于库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般 Linux 系统把 /lib 和 /usr/lib 两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中。设置库文件的搜索路径有下列两种方式,可任选其一使用:(http://hi.baidu.com/jiklong2005/blog/item/6ddb962a21defe86023bf69a.html

http://www.iteye.com/topic/261176

(1). 在环境变量 LD_LIBRARY_PATH 中指明库的搜索路径。

设置方式:

export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

可以用下面的命令查看 LD_LIBRAY_PATH 的设置内容:

echo $LD_LIBRARY_PATH

(2). 在 /etc/ld.so.conf 文件中添加库的搜索路径。(或者在/etc/ld.so.conf.d 下新建一个.conf文件,将搜索路径一行一个加入-junziyang)

Sudo mkdir -p /root/test/env/lib

mv libpos.so /root/test/env/lib

编辑配置文件/etc/ld.so.conf,在该文件中追加一行"/root/test/conf/lib"。

/sbin/ldconfig

将自己可能存放库文件的路径都加入到/etc/ld.so.conf中是明智的选择添加方法也极其简单,将库文件的绝对路径直接写进去就OK了,一行一个。例如:

/usr/X11R6/lib

/usr/local/lib

/opt/lib

需要注意的是:这种搜索路径的设置方式对于程序连接时的库(包括共享库和静态库)的定位已经足够了,但是对于使用了共享库的程序的执行还是不够的。这是因为为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,所以是直接读取库列表文件 /etc/ld.so.cache 从中进行搜索的。/etc/ld.so.cache 是一个非文本的数据文件,不能直接编辑,它是根据 /etc/ld.so.conf 中设置的搜索路径由 /sbin/ldconfig 命令将这些搜索路径下的共享库文件集中在一起而生成的(ldconfig
命令要以 root 权限执行)。

因此,为了保证程序执行时对库的定位,在 /etc/ld.so.conf 中进行了库搜索路径的设置之后,还必须要运行 /sbin/ldconfig 命令更新 /etc/ld.so.cache 文件之后才可以。ldconfig ,简单的说,它的作用就是将/etc/ld.so.conf列出的路径下的库文件缓存到/etc/ld.so.cache 以供使用。因此当安装完一些库文件,(例如刚安装好glib),或者修改ld.so.conf增加新的库路径后,需要运行一下 /sbin/ldconfig使所有的库文件都被缓存到ld.so.cache中,如果没做,即使库文件明明就在/usr/lib下的,也是不会被使用的,结果编译过程中抱错,缺少xxx库,去查看发现明明就在那放着,搞的想大骂computer蠢猪一个。

在程序连接时,对于库文件(静态库和共享库)的搜索路径,除了上面的设置方式之外,还可以通过 -L 参数显式指定。因为用 -L 设置的路径将被优先搜索,所以在连接的时候通常都会以这种方式直接指定要连接的库的路径。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux