GCC与静态库、共享库以及动态加载库

1.      GCC中不同类型的文件:

后缀

内容

.a

 静态对象库文件

.i

已经进行预处理的C源文件

.o

对象文件(-c)

.s

汇编语言代码(-S)

.so

共享对象库文件

 

2.      常见命令行选项与结果:

命令行

结果

gcc helloworld.c

默认的gcc行为是将源文件编译生成对象文件,并连接并产生一个可执行文件,最后删除中间产生的对象文件。产生可执行文件,不过名称为默认的a.out.

gcc helloworld.c –o helloworld

-o选项用来指定最后产生的可执行文件名

gcc -c helloworld.c

默认产生文件名为helloworld.o的对象文件,同样可以用-o选项指定对象文件名;值得注意的是,在一条命令中可以同时产生多个对象。

gcc hellomain.c sayhello.c -o hello

将相互存在依赖关系的文件通过编译链接最后生成可执行文件,以本例来讲,前者依赖后者。

gcc -E helloworld.c

产生预处理代码,默认情况下输出到标准输出,同样可以通过-o选项指定输出到哪个文件中,如helloworld.i

gcc -S helloworld.c

产生汇编语言代码

 

3.      静态库、共享库以及动态加载库

(1) 静态库

静态库是一些目标文件的集合,库文件以”.a”结尾,连接器会将应用程序所需要用到的代码拷贝到应用程序中.

【如何创建与使用静态库】

a>    首先生成目标文件: gcc -Wall -cmax.c

b>    创建静态库:ar rcs libmax.a max.o (可以根据ar –tlibmax.a查看静态库中所包含的目标文件)

c>    使用静态库:gcc hello_world.c libmax.a-o hello_world 或

gcc hello_world.c –L. –lmax -o hello_world

在使用静态库的过程中,可以指定静态库的全名,或者调用-l选项指定静态库的缩写名(静态库全名一般是以lib开头,后面跟静态库的缩写名),但是要注意的是,在这种情况下,最好是自己通过-L选项增加库搜索路径,否则编译的时候可能会出现库搜索不到的错误。另外一点,-l选项必须放在需要编译的文件之后,否则也会出现编译错误!

d>    静态库的优缺点:

静态库增加了应用程序的大小,另外在处理静态库更新问题上需要花费更多的重编译代价(recompile),但是理论上,静态库应该比共享库或者动态加载库运行更快(1-5%),因为它减少了在程序运行才去加载库的开销

(2)  共享库:

       与静态库不同的是,共享库在链接阶段并不需要拷贝所需使用的代码,而只是做些参考标记,然后在程序启动时加载所需要的库文件,因此,对比静态库,链接共享库的应用程序小得多。

【共享库的名字】

       跟共享库相关的名字有三个,分别是soname, real name和linker name,其中soname一般用来提供版本的兼容相关信息,real name则是包含实际共享库代码的文件名,linker name则是用于应用程序链接时候的一个搜索名。一般来讲,soname的命名以lib作为前缀,后者则是库名,接着是”.so”,最后则包含着周期数以及递增的版本信息它一般作为real name的符号链接;real name则是在soname的基础上加上一个周期数以及最小版本信息,和另一个周期数以及发行版本号(releasenumber).linker name则是在soname的基础上去掉了版本号,linker namesoname的符号链接

 【如何创建与使用共享库】

a>    首先创建对象文件:gcc -Wall -fpic-c max.c -fpic选项是一种编译器标志,表示产生与内存位置无关的代码,即在产生代码中的过程中全部使用相对地址,而不要使用绝对         地址,其中pic代表的是position independent code.

b>    创建共享库:

gcc -shared -Wl,-soname,libmax.so.1-olibmax.so.1.0 max.o

mv libmax.so.1.0 /opt/lib

ln -sf /opt/lib/libmax.so.1.0/opt/lib/libmax.so.1 (这条语句可以有ldonfig–n dir命令来完成)

ln -sf /opt/lib/libmax.so.1 /opt/lib/libmax.so

-shared选项是一种编译器标志,用来创建共享库,soname选项则是链接器标志,用来生成soname,在本例中,libmax.so.1是soname, libmax.so.1.0则是real name, libmax.so则是linker name.

【引申】共享库命名与版本兼容问题:

       以上面命名为例,若某个API发生改变,则所创建共享库的realname的主版本号应该发生改变,如此时,变成libmax.so.2.0, 若只是对函数升级了函数库而功能没有发生改变,则只需改变次版本号,如此时变成libmax.so.1.1, 这样可以做到版本兼容.

 c> 使用共享库:

       gcc -Wall -L/opt/lib hello_word.c -lmax-ohello_world

       使用方法类似于静态库,但是在应用程序运行的时候,却有着本质的区别,例如如果我们现在运行此时生成的应用程序hello_world,则会出现下图的错误信息,那是什么原因呢?正如前面所讲,程序在运行时还得需要加载所需要的库文件,此时不能找到库文件路径,处理这种错误可以采取以下两个方法:

设置LD_LIBRARY_PATH环境变量,可以用来配置共享库的搜索路径:

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH(将当前路径加入搜索目录)

配置-rpath编译选项用来表示增加一个目录到运行时库中,具体方法如下:

       gcc -shared-Wl,-soname,libmax.so.1,-rpath . -o libmax.so.1.0 max.o

d> 共享库的优缺点:

       对比静态库,共享库在链接阶段只是对所需代码做些标识,在程序启动时才会加载,这样减少了目标应用程序的大小,通过soname,可以做到多版本的兼容,这样每次升级也不需要将原代码全部重新编译,免除了在升级过程中重编译带来的开销;但是,因为需要在程序运行阶段加载共享库,这样势必需要付出运行期间的库加载代价。

e>    共享库相关的工具:

ldd name-of-executable  显示执行文件相依赖的共享库

nm name-of-library 显示库(静态或者动态)相关的标识符(symbol)

(3)动态加载库 (Dynamically loaded libraries)

       是指在程序运行过程中可以加载的函数库,而不像共享库是在程序启动的时候加载。DLL对实现插件和模块非常实用,因为它们运行程序在允许时等待插件的加载,动态加载库有自己的一套API接口去完成打开、查找符号,处理出错、关闭加载库等功能。

       void*dlopen (const char *libname, int flag);

       dlopen必须在dlerror, dlsym以及dlclose函数之前调用,表示要将共享库加载到内存中。若所加载的共享库还需要依赖其它库,则必须首先加载依赖库。若dlopen操作失败,则返回NULL,若库已经被加载过,则会返回同样的函数句柄,参数libname一般指库的全路径,这样直接加载库文件,若只是指定了库文件名,则会按照以下顺序进行库的查找:

a.      根据环境变量LD_LIBRARY_PATH查找;

b.      根据/etc/ld/so.cache目录查找;

c.      依次在/lib和/usr/lib目录查找;

         flag参数表示处理未定义函数的方式,可以使用RTLD_LAZYRTLD_NOW,前者表示暂时不处理未定义的函数,先把库加载在内存中,等遇到未定义的函数再说;后者表示立马检查是否存在未定义的函数,若存在,则以失败告终。

       void*dlsym(void *handle, const char *symbol);

       dlsym可以获得指定函数(根据symbol)在内存中的位置(指针)。若找不到函数,则返回NULL。

       Int dlclose(void *);

       dlclose可以关闭一个已经加载的库,若存在析构函数,则析构函数会调用。

 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值